如何使用 Git 进行高效的版本控制
Git 是一款分布式版本控制系统,于 2005 年由 Linus Torvalds 创造并开源发布。Git 的设计初衷是为了更好地管理 Linux 内核代码,它的分布式特性使得多人协作开发和大型项目的版本控制变得更加高效和可靠。现在,Git 已经成为了开源社区和软件开发领域中最受欢迎的版本控制工具之一。
Git 的基本概念包括:
版本控制
版本控制是指对文件或代码的修改历史进行记录和管理,以便于多人协作开发和版本回溯。Git 通过记录每次提交的快照来实现版本控制,并提供了一系列的命令和工具来管理版本库。
分支
分支是指从一个代码库中派生出来的独立代码线。在 Git 中,每个分支都是一个独立的代码库,可以在分支上进行修改和提交,而不影响主线代码的开发。分支在多人协作和复杂项目的版本控制中起到了重要作用。
提交
提交是指将代码库中的修改保存到版本库中的操作。每次提交都会创建一个新的版本快照,并记录下该次提交的作者、时间、提交信息等相关信息。
远程仓库
远程仓库是指存放在网络上的代码库,通常由多人协作开发的团队使用。Git 通过远程仓库来实现分布式版本控制,可以将代码库复制到本地进行修改和提交,然后将修改推送到远程仓库中。
合并
合并是指将两个不同的代码库中的修改合并到一个新的代码库中的操作。在多人协作开发中,当两个人在同一个文件的同一个位置做了修改时,就会产生冲突。Git 提供了合并功能来解决冲突,可以将两个不同的修改合并到一个新的代码库中。
补丁
补丁是指在不修改原有代码的情况下,通过对代码库进行修改来实现功能的操作。在 Git 中,补丁通常由 diff
命令生成,可以通过 patch
命令来应用补丁。
Git 的这些基本概念是 Git 理解和使用的基础,掌握这些概念有助于更好地理解 Git 的命令和工作原理。
Git 基本命令
git init
git init
是一个初始化 Git 仓库的命令。在执行该命令后,Git 会在当前目录下创建一个新的 .git
目录,这个目录包含了 Git 仓库所需要的所有元数据,比如说分支(branch)、提交(commit)、远程仓库(remote)等。
常用参数:
--bare
: 初始化一个裸仓库,即不包含工作目录。--template=<template_directory>
: 使用指定目录作为模板来初始化仓库。
举例说明:
- 在当前目录下创建一个新的Git仓库,可以使用以下命令:
1 | $ git init |
- 初始化一个裸仓库:
1 | $ git init --bare |
- 使用指定目录作为模板来初始化仓库:
1 | $ git init --template=<template_directory> |
git add
git add
命令将更改的文件添加到 Git 仓库的暂存区,使这些更改准备好被记录在一个新的提交中。
常用参数包括:
-u
或--update
:将已跟踪的文件的更改添加到暂存区,同时不包括新文件和被删除的文件-A
或--all
:将所有更改,包括新文件、被删除的文件和已跟踪的文件的更改添加到暂存区<pathspec>
:指定要添加的文件路径,可以使用通配符匹配多个文件
例如,我们新建了一个名为 example.txt
的文件,我们可以使用以下命令将该文件添加到暂存区:
1 | $ git add example.txt |
如果我们想要将所有更改的文件都添加到暂存区,可以使用以下命令:
1 | $ git add -A |
如果我们只想要将已跟踪的文件的更改添加到暂存区,同时不包括新文件和被删除的文件,可以使用以下命令:
1 | $ git add -u |
git commit
git commit
命令用于将已暂存的更改保存为一个新的提交对象。它是 Git 中最基本的命令之一,经常用于提交代码和记录版本变更历史。以下是该命令的一些常用参数:
-m
:使用一行消息来描述本次提交,例如git commit -m "fix bug in login page"
。-a
:跳过git add
步骤直接提交已经被追踪的文件更改,不包括未被追踪的文件,例如git commit -a -m "update readme file"
。-am
:上述两个参数的合并形式,可以同时提交已经被追踪的文件更改并附带一行消息,例如git commit -am "update readme file"
。--amend
:修改上一个提交,通常用于修改提交消息或添加遗漏的文件,例如git commit --amend -m "update readme file"
。
示例:
提交所有暂存的更改并附带一行消息:
1
$ git commit -m "add new feature"
跳过
git add
步骤,提交已经被追踪的文件更改并附带一行消息:1
$ git commit -a -m "fix bug"
修改上一个提交的提交消息:
1
$ git commit --amend -m "fix typo"
git status
git status
命令用于查看当前工作区状态和暂存区状态,可以帮助开发者确定下一步操作。命令格式为:
1 | $ git status [-s|--short] [--branch] [--porcelain] [--long] [--untracked-files[=<mode>]] [--ignore-submodules[=<when>]] [--ignored] [<pathspec>...] |
常用参数包括:
-s|--short
:使用短格式输出。--branch
:在输出中包括分支信息。--porcelain
:使用机器可读格式输出。--long
:使用详细格式输出。--untracked-files[=<mode>]
:包括未跟踪的文件。mode
可选参数包括no
、normal
和all
。--ignore-submodules[=<when>]
:是否忽略子模块状态。when
可选参数包括none
、untracked
、dirty
和all
。--ignored
:包括被忽略的文件。<pathspec>...
:指定要显示状态的文件。
例如:
1 | # 查看当前工作区和暂存区状态 |
git status
命令的输出结果通常包括:
- 当前所在的分支
- 已修改但未添加至暂存区的文件
- 已添加至暂存区但未提交的文件
- 上游分支与本地分支之间的差异
- 各个分支之间的差异
- 存在冲突的文件
- 忽略的文件
根据输出结果,可以进行相应的操作,如添加、提交、撤销等。
git diff
git diff
命令可以用于比较工作目录和暂存区域的差异,或者比较已提交的版本和暂存区域的差异。下面介绍几种常用的用法及其参数:
比较工作目录和暂存区域的差异
1
$ git diff
运行这个命令会显示工作目录和暂存区域的差异。如果没有任何参数,
git diff
会显示所有已修改但未添加到暂存区域的文件的差异。举个例子,假设我们修改了
file.txt
文件并保存了修改,但是还没有使用git add
将修改添加到暂存区域。此时,如果运行git diff
命令,会显示出file.txt
文件的差异。比较暂存区域和已提交的版本的差异
1
$ git diff --staged
这个命令会显示暂存区域和已提交的版本的差异。如果没有任何参数,
git diff
会显示所有已修改但未添加到暂存区域的文件的差异。举个例子,假设我们修改了
file.txt
文件并使用git add
将修改添加到暂存区域,但是还没有使用git commit
将修改提交到版本库。此时,如果运行git diff --staged
命令,会显示出file.txt
文件的差异。比较两个已提交的版本之间的差异
1
$ git diff <commit1> <commit2>
这个命令会显示两个已提交的版本之间的差异。
<commit1>
和<commit2>
可以是版本号、分支名或标签名等。举个例子,假设我们想要比较
v1.0
标签和v2.0
标签之间的差异,可以使用以下命令:1
$ git diff v1.0 v2.0
显示文件的修改历史记录
1
$ git log <filename>
这个命令会显示指定文件的修改历史记录。
举个例子,假设我们想要查看
file.txt
文件的修改历史记录,可以使用以下命令:1
$ git log file.txt
以上就是git diff
命令的常用用法及其参数的介绍,可以帮助我们更好地了解工作目录、暂存区域和版本库之间的差异,以及文件的修改历史记录。
git log
git log
用于查看 Git 仓库中的提交记录。可以使用该命令查看当前分支的提交记录,也可以查看某个分支或某个特定提交的提交记录。默认情况下,git log
会按照时间顺序列出所有提交记录。
常用的参数包括:
-n
或--max-count=<n>
:限制显示的提交记录数量。--since
和--until
:按时间过滤提交记录。--author
:按作者过滤提交记录。--grep
:按提交说明过滤提交记录。
以下是一些使用示例:
显示当前分支的所有提交记录:
1
$ git log
显示指定分支的所有提交记录:
1
$ git log <branch-name>
显示某个文件的提交记录:
1
$ git log <file-name>
限制显示最近的 n 条提交记录:
1
$ git log -n <n>
显示某个时间段内的提交记录:
1
$ git log --since=<date1> --until=<date2>
显示某个作者的提交记录:
1
$ git log --author=<author-name>
按提交说明搜索提交记录:
1
$ git log --grep=<commit-message>
git log
常用于查看项目的提交历史和了解每个提交所做的更改。通过对提交记录的分析,可以更好地理解项目的发展过程,并且可以帮助开发人员更有效地进行合并和回滚操作。
Git 分支操作命令
git branch
git branch 命令用于列出、创建、重命名或删除分支。下面详细介绍其常用参数和示例:
常用参数:
-a
:列出所有本地和远程分支。-d
或--delete
:删除指定的分支。-D
:强制删除指定的分支。-m
或--move
:重命名指定的分支。-M
:强制重命名指定的分支。
示例:
- 列出本地分支
1 | $ git branch |
输出结果为:
1 | master |
上述命令表示列出当前所在仓库的所有本地分支,*
符号代表当前所在分支。
- 创建分支
1 | $ git branch feature |
上述命令表示创建一个名为 feature
的分支。
- 切换分支
1 | $ git checkout feature |
上述命令表示切换到名为 feature
的分支。
- 删除分支
1 | $ git branch -d feature |
上述命令表示删除名为 feature
的分支,如果该分支还有未合并的提交,删除操作将失败。如果一定要删除该分支,可以使用 -D
参数,它会强制删除分支:
1 | $ git branch -D feature |
- 重命名分支
1 | $ git branch -m feature new-feature |
上述命令表示将名为 feature
的分支重命名为 new-feature
。
- 列出所有分支
1 | $ git branch -a |
上述命令表示列出当前所在仓库的所有本地和远程分支。
git checkout
git checkout
命令用于切换分支或还原工作目录中的文件。它的常用参数包括:
-b <new_branch>
: 创建并切换到新分支。-f
或--force
: 强制切换分支或还原文件,可能会丢失未保存的更改。-p
: 交互式地选择要丢弃的更改。<commit>
: 切换到指定的提交,创建一个分离头指针。
以下是一些示例:
切换分支:
1
2$ git checkout dev
Switched to branch 'dev'创建并切换到新分支:
1
2$ git checkout -b feature-branch
Switched to a new branch 'feature-branch'还原单个文件:
1
$ git checkout file.txt
还原整个目录:
1
$ git checkout .
切换到特定提交:
1
2
3
4
5
6
7
8
9
10
11
12
13$ git checkout 1a2b3c4d
Note: checking out '1a2b3c4d'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at 1a2b3c4d... Initial commit
git merge
git merge
命令用于将一个分支的更改合并到当前分支中。它有以下常用参数:
-m
:指定合并时使用的提交信息。如果省略该参数,Git 将自动使用默认信息。--no-ff
:创建一个新的合并提交,而不是使用快进模式合并分支。这将保留分支历史信息,并防止将更改直接合并到主线分支。--abort
:取消合并操作,恢复到合并之前的状态。--continue
:在解决合并冲突后继续进行合并操作。
下面是一个示例:
1 | # 创建一个新分支 |
在这个示例中,我们首先创建了一个名为 my-branch
的新分支,并在该分支上进行了一些更改和提交。然后我们切换到主分支,并使用 git merge
命令将 my-branch
分支合并到主分支中。如果发生合并冲突,我们需要解决冲突并继续合并操作。最后,我们完成了合并并提交了一个新的合并提交。
git rebase
git rebase
是 Git 中一个非常强大的命令,它可以在合并分支的时候,将多个提交整合成一次提交,从而保持分支的整洁性。
具体来说,git rebase
的作用是将当前分支上的提交复制到另一个分支上,这个过程中,Git 会先将当前分支的提交暂存起来,然后切换到目标分支,将暂存的提交依次应用到目标分支上,并产生新的提交,最后再切换回原来的分支。
常用参数包括:
-i
:交互式 rebase,可以手动调整提交的顺序、合并提交等操作。-p
:保留 commit 中的时间戳、作者等信息。-s/--strategy
:选择 rebase 的策略,常用的策略有merge
(默认)、ours
、theirs
等。
举个例子,假设我们有一个分支 feature-branch
,它基于 master
分支并已经存在多个提交。现在我们需要将 master
分支的更改合并到 feature-branch
,可以通过以下命令:
1 | $ git checkout feature-branch |
这样,Git 会将 feature-branch
分支上的所有提交复制到 master
分支上,并在 master
分支上产生一次新的提交,保持分支的整洁性。在这个过程中,如果出现冲突,需要手动解决冲突并使用 git add
命令来标记解决冲突后的文件,然后使用 git rebase --continue
命令来继续 rebase 过程。
Git 远程仓库命令
git clone
git clone
命令用于从远程 Git 存储库克隆项目到本地机器上。它会复制整个存储库,包括所有分支和版本历史记录。这使得它成为在新机器上开始工作或与他人共享项目的理想选择。
该命令的基本语法如下所示:
1 | $ git clone <url> |
其中,url
是要克隆的 Git 存储库的 URL 地址。还可以通过添加其他选项来定制克隆过程:
-b
或--branch
:指定要克隆的分支,默认为master
。-depth
:仅克隆指定深度的历史记录,以减少下载时间和磁盘空间的使用量。--recursive
:在克隆时也初始化和更新任何子模块。
以下是一些常见的用法和例子:
- 克隆名为
my-project
的存储库:
1 | $ git clone https://github.com/my-username/my-project.git |
- 克隆名为
my-project
的存储库中的development
分支:
1 | $ git clone --branch development https://github.com/my-username/my-project.git |
- 仅克隆存储库的最近 5 次提交:
1 | $ git clone --depth 5 https://github.com/my-username/my-project.git |
- 克隆一个包含子模块的存储库:
1 | $ git clone --recursive https://github.com/my-username/my-project.git |
git push
git push是用于将本地代码推送到远程代码库的命令。它的一般语法为:
1 | $ git push <remote> <branch> |
其中<remote>
是要推送到的远程代码库的名称,<branch>
是要推送的本地分支名称。如果省略<branch>
,则会将当前本地分支推送到远程代码库的同名分支。
常用参数:
-u
:将本地分支与远程分支关联,这样以后就可以使用git push
命令推送代码了。--force
:强制推送本地代码到远程分支,即使远程分支的代码比本地代码更新。
示例:
将本地分支
main
推送到远程代码库origin
的main
分支:1
$ git push origin main
将本地分支
feature
推送到远程代码库origin
的dev
分支,并将本地分支与远程分支关联:1
$ git push -u origin feature:dev
强制将本地分支
main
推送到远程代码库origin
的main
分支,即使远程分支的代码比本地代码更新:1
$ git push --force origin main
git pull
git pull
命令用于从远程仓库中获取最新的代码并合并到本地分支中,实现代码同步。它实际上是git fetch
和git merge
两个命令的组合。
常用参数:
-r
或--rebase
:使用git pull --rebase
可以将本地的修改放到最新的远程提交后面,以避免由于本地修改引起的合并冲突。
举例说明:
假设你的本地分支与远程分支存在差异,并且你想要获取远程分支的最新代码并将其合并到本地分支中,可以执行以下命令:
1 | $ git pull origin master |
这会从远程仓库中获取master分支的最新代码并将其合并到当前的本地分支。如果本地分支和远程分支之间有冲突,则需要解决冲突后再次提交。如果你想使用rebase方式进行合并,可以使用git pull --rebase origin master
命令。
git fetch
git fetch
命令用于从远程仓库中下载最新的代码到本地仓库,但是不会自动将代码合并到当前分支中。它会将远程仓库的代码更新到本地仓库的“远程跟踪分支”中。可以使用 git merge
或者 git rebase
将这些更新合并到本地分支中。
常用的参数包括:
<remote>
:指定要获取代码的远程仓库,默认为origin
。<branch>
:指定要获取的分支名。默认为当前所在分支。
举例来说,要从远程仓库 origin
的 main
分支中获取最新代码,可以使用以下命令:
1 | $ git fetch origin main |
这将会将最新的 main
分支代码更新到本地仓库中的 origin/main
远程跟踪分支中,但是不会将代码合并到当前分支中。如果需要将这些更新合并到当前分支中,可以使用 git merge
或 git rebase
命令。
Git 高级命令
git stash
git stash
命令可用于将未提交的更改保存到一个临时区域(称为“stash”),以便稍后恢复。这在需要快速更改分支或修复错误时非常有用。
常用的参数和选项包括:
git stash save [<message>]
:将未提交的更改存储到一个新的stash,并可选地为存储添加描述信息。git stash list
:列出当前存储的stash。git stash apply [<stash>]
:将最新的stash应用到当前分支。如果指定了一个stash,将应用指定的stash。git stash drop [<stash>]
:从列表中删除指定的stash。如果没有指定,则删除最新的stash。git stash pop [<stash>]
:将最新的stash应用到当前分支,并从列表中删除它。如果指定了一个stash,将应用指定的stash并将其从列表中删除。git stash branch <branchname> [<stash>]
:从指定的stash创建一个新分支,并将其应用到该分支。如果没有指定,则使用最新的stash。
以下是一个使用git stash
命令的示例:
修改文件并添加到暂存区
1
$ git add .
对文件进行修改
1
$ vim file.txt
对文件进行另一些修改,但还没有提交
1
$ vim file.txt
现在您需要切换分支,但不能提交尚未完成的工作。 使用git stash将更改保存在暂存区。
1
$ git stash save "my work in progress"
您可以通过git stash list查看保存的stash
1
2$ git stash list
stash@{0}: On master: my work in progress现在可以切换到其他分支,进行其他工作。
1
$ git checkout other-branch
回到之前的分支,并应用之前保存的stash
1
2$ git checkout master
$ git stash apply或者,您可以创建一个新分支并将stash应用到该分支
1
$ git stash branch new-branch
git cherry-pick
git cherry-pick
命令用于将指定的提交应用于当前分支,它可以将单个或多个提交从其他分支或当前分支的不同位置应用到当前分支。它可以用来复制特定的提交或解决特定的问题。
该命令的基本语法为:
1 | $ git cherry-pick <commit-hash> |
其中 <commit-hash>
是要应用的提交的哈希值。可以使用 git log
命令来查找要应用的提交的哈希值。另外,可以一次选择多个提交来应用。
git cherry-pick
命令还支持以下常用参数:
-e
或--edit
:打开编辑器以编辑提交信息;-n
或--no-commit
:将更改应用到工作目录和暂存区,但不自动提交;-x
:在提交信息中包含从哪个提交进行的 Cherry-pick 信息;-s
或--signoff
:在提交信息末尾添加 Signed-off-by 行。
下面是一个使用 git cherry-pick
命令的示例:
假设我们有一个名为
feature-branch
的分支,我们要将该分支上的一个提交应用到main
分支上。首先,我们需要切换到main
分支:1
$ git checkout main
然后,我们可以使用
git log
命令来查找要应用的提交的哈希值:1
$ git log feature-branch
找到要应用的提交的哈希值后,我们可以使用
git cherry-pick
命令将其应用到当前分支:1
$ git cherry-pick <commit-hash>
如果该提交没有冲突,则会自动应用。如果有冲突,则需要手动解决冲突并使用
git add
命令将更改标记为已解决。然后,可以使用git cherry-pick --continue
命令继续应用提交。如果想放弃 cherry-pick,则可以使用git cherry-pick --abort
命令。
git reset
git reset
命令可以将 HEAD 指针指向指定的提交记录,并把这个提交记录之后的提交记录从暂存区和工作区中移除。它可以用来撤销已提交的更改,恢复之前的版本。常用参数如下:
--soft
:将 HEAD 指针移动到指定的提交记录,但是不会修改暂存区和工作区,这意味着你可以重新提交这些更改。--mixed
:这是默认选项,将 HEAD 指针移动到指定的提交记录,并将暂存区恢复为该提交的状态,但不修改工作区,这意味着你需要再次使用git add
命令将需要提交的更改加入暂存区。--hard
:将 HEAD 指针移动到指定的提交记录,并将暂存区和工作区都恢复为该提交的状态。这意味着之前的更改将被完全删除,慎用该选项。
常见用法包括:
- 恢复到之前某个提交版本,例如
git reset HEAD~1
表示回到上一个提交版本。 - 撤销之前的提交,例如
git reset HEAD~1 --soft
表示将 HEAD 指针指向上一个提交版本,但是保留之前提交的更改,可以重新修改并再次提交。
注意,git reset
命令改变了 Git 的提交历史记录,因此如果已经推送到远程仓库,需要慎重使用该命令,否则可能导致协作中的其他人产生困惑。
git revert
git revert
命令用于撤销一个或多个提交,它会生成一个新的提交,该提交将逆转先前的提交更改。
常用的参数包括:
-n, --no-commit
: 撤销提交时不自动生成新的提交。-m parent-num
: 用于撤销一个合并提交,指定合并提交中要撤销的父提交的序号。
例如,假设我们有一个提交历史记录如下:
1 | * 1f024d6 (HEAD -> main) add file3.txt |
如果我们想要撤销 add file2.txt
这个提交,可以使用以下命令:
1 | $ git revert 6c66a89 |
Git会自动生成一个新的提交,将之前 add file2.txt
提交中的更改逆转。这将导致提交历史记录如下所示:
1 | * a438432 (HEAD -> main) Revert "add file2.txt" |
注意,使用 git revert
不会从代码库中删除任何东西,只是生成一个新的提交来撤销之前的更改。如果要完全删除某个提交及其更改,可以使用 git reset
或 git rebase
命令。
总结
本文介绍了 Git 常用的基础命令及其用法,并给出了相应的示例。
Git初始化
git init
初始化一个 Git 仓库
Git管理文件
git add [file]
将文件添加到 Git 索引中git commit -m "message"
提交索引中的文件并添加提交消息
Git状态查询
git status
显示 Git 仓库的当前状态git diff
显示未暂存文件与上次提交版本的差异git log
显示提交日志
Git分支管理
git branch
显示本地分支列表git checkout [branch]
切换到目标分支git merge [branch]
合并指定分支到当前分支git rebase [branch]
将当前分支变基到目标分支
Git远程仓库管理
git clone [url]
从远程仓库克隆代码到本地git push [remote] [branch]
将本地分支推送到远程仓库git pull [remote] [branch]
从远程仓库拉取最新代码到本地git fetch [remote]
从远程仓库获取最新代码
Git其他命令
git stash
暂存当前工作进度git cherry-pick [commit]
从指定提交中提取更改并应用于当前分支git reset [commit]
撤销到指定的提交git revert [commit]
撤销指定的提交
推荐最佳实践
- 使用分支进行开发,不要直接在
master
分支上进行开发。 - 提交信息要详细,不要简单地写 “修改” 或 “更新” 等不具体的描述。
- 定期将本地分支与远程分支同步,避免出现代码冲突。
- 使用 Git 工作流程管理开发流程,如 Git Flow 等。
参考资料
- Pro Git book
- Git official documentation