Git教程
Git教程
1. 安装Git
1.1 在Linux上安装Git
可以在终端中输入git
来查看系统是否安装git:
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git
- Debian或Ubuntu Linux安装git:
sudo apt-get install git
- 老版本的Debisn或Ubuntu Linux安装git:
sudo apt-get install git-core
- 其他Linux版本:可以直接通过源码安装,从官网下载源码,然后解压,依次输入:
./config
、make
、sudo make install
1.2 在windows上安装Git
直接从官网上下载Git,默认安装即可。
安装完成之后,打开Git bash,需要在命令行中输入以下命令配置用户名称和邮件:
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
可以使用git config -l
命令查看git配置项详情:
$ git config -l
diff.astextplain.textconv=astextplain
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.required=true
http.sslbackend=openssl
http.sslcainfo=C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
core.autocrlf=true
core.fscache=true
core.symlinks=false
pull.rebase=false
credential.helper=manager
user.name=xxx
user.email=xxx@example.com
2. 创建版本库
版本库又叫仓库(reposity),可以简单地把它理解成一个目录,这个目录里的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以还原。
2.1 创建一个版本库:
- 在合适的路径下,创建一个空目录:
$ mkdir learngit
$ cd learngit
$ pwd
D:\Dev\Projects\Test_Projects\learngit
- 使用
git init
命令把这个目录变成Git可以管理的仓库
$ git init
Initialized empty Git repository in D:/Dev/Projects/Test_Projects/learngit/.git/
此时,Git已经把仓库建好了,但是现在是空的仓库,此时,当前目录下多了一个.git
目录,这个目录是Git用来跟踪管理版本库的,通常情况下该目录是隐藏的(ls -ah
),不要对.git
目录做修改,否则会破坏该仓库。
Git只能跟踪文本文件的改动,如txt文本、源代码、md文档等,Git可以告诉我们对代码仓库的每次改动,如对某源代码文件的某行进行了怎样的修改,对于图片、视频、音频等二进制文件,虽然也受Git的管理,但Git无法追踪这些二进制文件的变化。
文本文件建议使用UTF-8编码。
2.2 添加文件到版本库
编写一个想上传上版本库的文件,之后将它放入版本库的目录(或子目录)下:
# ReadMe.md
This is a introduce of the respository.
- 把文件添加到版本库:
$ git add ReadMe.md
- 使用命令
git commit -m "<comment>"
把文件提交到仓库:
$ git commit -m "write a readme.md"
[master (root-commit) 7de169f] write a readme.md
1 file changed, 1 insertion(+)
create mode 100644 ReadMe.md
- 一次添加多个文件到版本库
$ git add file1.txt file2.txt file3.txt
3. 版本管理
3.1 查看代码仓库的状态
git status
命令可以查看当前代码库的状态:
$ git status
On branch master
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: ReadMe.md
no changes added to commit (use "git add" and/or "git commit -a")
上面的结果说明ReadMe.md文件被修改了,可以使用git diff <filename>
查看文件的具体修改:
$ git diff ReadMe.md
diff --git a/ReadMe.md b/ReadMe.md
index ed0ed65..e35eac2 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -1 +1,2 @@
This is a introduce of the repository.
+Modify test.
修改之后需要再次添加修改的文件:
$ git add ReadMe.md
再次查看代码库状态:
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: ReadMe.md
可以看到提示了将会提交的修改是ReadMe.md
修改完成之后,可以把修改提交:
$ git commit -m "ReadMe.md Modified"
[master 660ec15] ReadMe.md Modified
1 file changed, 2 insertions(+), 1 deletion(-)
提交完成之后,再次查看代码库,Git将会告诉我们当前没有修改需要提交:
$ git status
On branch master
nothing to commit, working tree clean
3.2 版本穿梭
使用git log
命令可以查看最近的git commit
记录:
$ git log
commit 660ec15d617cb2698ee1819db5ee860f860660f8 (HEAD -> master)
Author: lingyulong <lingyulong@huawei.com>
Date: Fri May 19 22:20:06 2023 +0800
ReadMe.md Modified
commit c05ea785001759ae79a3991b250e97f96e9fbcf5
Author: lingyulong <lingyulong@huawei.com>
Date: Fri May 19 21:36:08 2023 +0800
ReadMe.md modify test
commit 7de169f5053e874ca34a4e5f908f9cc256c6bf92
Author: lingyulong <lingyulong@huawei.com>
Date: Wed May 10 09:46:31 2023 +0800
write a readme.md
或者加上--pretty=oneline
参数简化输出信息:
$ git log --pretty=oneline
660ec15d617cb2698ee1819db5ee860f860660f8 (HEAD -> master) ReadMe.md Modified
c05ea785001759ae79a3991b250e97f96e9fbcf5 ReadMe.md modify test
7de169f5053e874ca34a4e5f908f9cc256c6bf92 write a readme.md
上面的每行commit提交的第一个长字符串其实是commit id
,它是SHA1算法计算出的大数字,用16进制表示;
HEAD
表示当前的版本,也是最新的提交,若想回到过去,可以使用git reset --hard HEAD^
命令(回退到上上个版本就是HEAD^^
):
注意:
git reset --hard
将会放弃所有更改!
$ git reset --hard HEAD^
HEAD is now at c05ea78 ReadMe.md modify test
此时再使用git log
查看现在代码库的版本状态:
$ git log
commit c05ea785001759ae79a3991b250e97f96e9fbcf5 (HEAD -> master)
Author: lingyulong <lingyulong@huawei.com>
Date: Fri May 19 21:36:08 2023 +0800
ReadMe.md modify test
commit 7de169f5053e874ca34a4e5f908f9cc256c6bf92
Author: lingyulong <lingyulong@huawei.com>
Date: Wed May 10 09:46:31 2023 +0800
write a readme.md
可以看到,之前最新的"ReadMe.md Modified"版本已经不见了,想要再重返未来可以根据commit id
回退:
$ git reset --hard 660ec
HEAD is now at 660ec15 ReadMe.md Modified
Git使用git reflog
来记录版本记录(包括已经回退前的未来的版本):
$ git reflog
660ec15 (HEAD -> master) HEAD@{0}: reset: moving to 660ec
c05ea78 HEAD@{1}: reset: moving to HEAD^
660ec15 (HEAD -> master) HEAD@{2}: commit: ReadMe.md Modified
c05ea78 HEAD@{3}: commit: ReadMe.md modify test
7de169f HEAD@{4}: commit (initial): write a readme.md
- 回到过去前,可以使用
git log
查看提交历史,以便回退到过去; - 若要重返未来,可以使用
git reflog
查看所有版本记录,以便重返未来;
3.3 工作区和暂存区
- 工作区:工作区就是代码的目录,如
learngit
文件夹就是一个工作区 - 暂存区:工作区中有一个隐藏的目录
.git
,它是Git的版本库,版本库中最重要的就是暂存区(stage
或index)、分支master
、和指向当前master
的指针HEAD
当我们把文件往Git版本库中添加的时候,实际上是分两步完成的:
- 第一步是用
git add
把文件添加进去,实际上就是把文件修改添加到暂存区; - 第二步是用
git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
git add
可以把文件添加到追踪名单中,没有被git add
的文件将是Untracked
的状态
3.4 管理修改
Git管理的是修改而不是文件!
对于以下流程:
第一次修改README.md -> git add README.md -> 第二次修改README.md -> git commit -m "first modification"
提交后的结果为README.md保持第一次修改时的状态,第二次修改并没有被提交,这是因为Git管理的是修改而不是文件——第一次修改通过git add
被放入了暂存区,但是第二次修改并没有通过git add
放入暂存区,因此git commit
提交暂存区的修改后,只有第一次修改被提交了!
3.5 撤销修改
可以使用git checkout -- <file>
来丢弃工作区的修改:
$ git checkout -- README.md # 注意--前后都有空格
git checkout --README.md
有两种意思:
- 一种是README.md自修改之后还没有被放到暂存区,现在撤销修改就回到和版本库一样的状态;
- 一种是README.md已经添加到暂存区之后,又做了修改,现在撤销修改就回到添加到暂存区的状态;
总之,git checkout --README.md
就是让文件README.md回到最近一次git commit
或者git add
时的状态
3.6 删除文件
通常情况下,若在文件管理器中把不需要的文件删了或使用rm
命令删了:
$ rm test.txt
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
此时,git将会知道删除了文件,因此工作区和版本库就不一致了
- 若是确实要删除该文件,那么就用
git rm
将文件删掉,并且git commit
:
$ git rm test.txt
$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
- 若是误删文件,则可以使用
git checkout -- <file>
来恢复文件:
$ git checkout -- test.txt
注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!
4. 远程仓库
4.1 创建和添加SSH秘钥
- 创建SSH Key
在Git bash中执行以下命令:
$ ssh-keygen -t rsa -C "youremail@example.com"
之后将会在用户的主目录下,生成.ssh
目录,目录下有id_rsa
和id_pub
文件
- 登录GitHub
打开"Account settings","SSH Keys"页面,点击"Add SSH Key",填上任意Title,在Key文本框里粘贴id_rsa.pub
文件中的内容:
4.2 添加远程仓库
目前,已经在本地有了一个Git仓库,可以在GitHub上再创建一个远程Git仓库,并且让这两个仓库远程同步。
- 登录GitHub,点击"Create a new repo",创建一个新的仓库:
- 目前GitHub上的这个新建的仓库还是空的,可以通过从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后把本地仓库的内容推送到GitHub仓库:
在本地的learngit仓库下运行以下命令:
$ git remote add origin git@github.com:<username>/learngit.git # 填上自己的用户名
远程仓库的名称默认就叫
origin
- 把本地库的所有内容推送到远程库上:
$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (20/20), 1.64 KiB | 560.00 KiB/s, done.
Total 20 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:michaelliao/learngit.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
把本地仓库的内容推送到远程仓库,是使用git push
命令,实际上是把当前分支master
推送到远程origin
仓库
由于远程库是空的,第一次推送
master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送给远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令,之后推送时,只要本地作了提交,就可以通过命令git push origin master
把本地master分支修改推送到远程GitHub仓库
4.3 删除远程库
若想删除远程库,可以使用git remote rm <name>
命令,使用前建议使用git remote -v
查看远程库信息:
$ git remote -v
origin git@github.com:n1rv2na/learn-git.git (fetch)
origin git@github.com:n1rv2na/learn-git.git (push)
之后可以根据仓库名来进行删除:
$ git remote rm origin
此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到GitHub,在后台页面找到删除按钮再删除!
4.4 克隆远程库
从零开发的最佳方式是先创建远程库,然后从远程库克隆:
首先登陆GitHub,然后通过4.2中的方式创建一个新的仓库;
然后使用命令git clone
克隆一个远程库的本地库:
$ git clone git@github.com:n1rv2na/learngit1.git
# git@使用的是ssh克隆,Git支持多种协议,也可以使用HTTPS克隆
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3
Receiving objects: 100% (3/3), done.
5. 分支管理
5.1 Git分支管理原理
- 一开始的时候,
master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步,随着开发者不断提交,master
分支的线也越来越长。
- 当创建新的分支,例如
dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
- 创建
dev
分支后,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
- 假如在
dev
上的工作完成了,就可以把dev
合并到master
上,Git合并就是把master
直接指向dev
的当前提交:
- 合并完分支后,甚至可以删除
dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,就剩下了一条master
分支:
5.2 新建分支
使用git checkout -b <branch name>
可以创建新的分支并且切换到该分支:
$ git checkout -b dev
Switched to a new branch 'dev'
-b参数相当于以下两条命令:
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
可以使用git branch
命令查看仓库当前分支:
$ git branch
* dev
master
git branch
命令会列出本地所有分支,当前分支前面会标一个*
号。
git branch --set-upstream-to=origin/<origin-branch> <local-branch>
该命令可以创建本地分支和远程分支的关联。
git branch -r
命令可以查看远程仓库的所有远程分支。
5.3 分支合并
可以在新建的dev
分支上正常提交:
$ echo "Creating a new branch named dev" >> README.md
$ git add README.md
$ git commit -m "branch test"
[dev b17d20e] branch test
1 file changed, 1 insertion(+)
此时,切回master
分支之后,将会发现刚刚对README.md做的修改不见了,因为那个提交是在dev
分支上,而master
分支此时的提交点并没有改变
可以使用git merge dev
命令将dev
分支上的工作合并到master
分支上:
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
git merge
命令用于合并指定分支到当前分支。合并后,再查看README.md
的内容,就可以看到,和dev
分支的最新提交是完全一样的。
注意到上面的Fast-forward
信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。
合并完成后,就可以删除dev
分支了:
$ git branch -d dev
Deleted branch dev (was b17d20e).
$ git branch
* master
- 切换分支
可以使用git checkout <branch name>
命令切换分支,也可以使用git switch
命令来切换分支:
创建并切换到新的dev
分支,可以使用:
$ git switch -c dev
直接切换到已有的分支,可以使用:
$ git switch master
5.4 删除分支
- 删除本地分支:
$ git branch --delete dev
或者:
$ git branch -d dev
使用-d删除分支前,必须要求dev分支已经被合并了,否则删除将会丢失修改;
若要强行删除,需要使用-D
参数:
$ git branch -D dev
- 删除远程分支:
$ git push origin -delete dev
5.5 解决冲突
合并分支时,可能会遇到冲突的情况:
新建分支feature1
:
$ git switch -c feature1
Switched to a new branch 'feature1'
修改README.txt的最后一行为:
Creating a new branch is quick AND simple.
然后在feature1
分支上提交:
$ git add readme.txt
$ git commit -m "AND simple"
[feature1 14096d0] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)
之后切回master
分支:
$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
在master
分支上修改README.txt的最后一行为:
Creating a new branch is quick & simple.
提交:
$ git add readme.txt
$ git commit -m "& simple"
[master 5dc6824] & simple
1 file changed, 1 insertion(+), 1 deletion(-)
现在master
分支和feature1
分支都有了新的提交,变成了这样:
这种情况下,Git无法快速合并,只能试图把各自分支的修改合并起来,但这种合并可能产生“冲突”:
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
Git告诉我们,readme.txt
文件存在冲突,必须手动解决冲突后再提交。
此时查看readme.txt
的内容, Git会用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
修改readme.txt
的最后一行为:
Creating a new branch is quick and simple.
之后重新commit
解决冲突后即可合并。
5.6 分支管理策略
Git Fast-forward
合并时,它会在合并分支时直接把当前分支指针移动到目标分支的最新提交,而不会在提交历史中创建新的合并提交,因此在删除分支后,就会丢掉分支信息!
如果要禁用Fast-forward
,可以使用git merge --no-ff
,禁用后,Git就会在merge的时候生成一个新的commit,这样就可以在分支历史上看出分支信息。
- 创建并切换dev分支:
$ git switch -c dev
Switched to a new branch 'dev'
- 修改readme.txt,并提交:
$ git add readme.txt
$ git commit -m "add merge"
[dev f52c633] add merge
1 file changed, 1 insertion(+)
- 切换回master分支:
$ git switch master
Switched to branch 'master'
- 以禁用
Fast forward
的方式合并创建一个commit:(以--no-ff
方式合并将会生成一个新的commit
,因此需要加上-m
描述)
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
分支策略:
在实际开发中,应当遵循以下基本原则进行分支管理:
- 首先,
master
分支应该是非常稳定的,仅用来发布新版本,平时不应在master上进行开发; - 其次,团队成员每个人都在各自的
dev
分支上进行开发,在发布版本的时候,再把dev分支合并到master上;
5.7 Bug分支
在软件开发中,遇到bug需要修复时,可以通过创建一个临时的bug分支来修复,修复后合并分支,再将临时分支删除即可;
当需要修复一个紧急的bug时,自然地可以创建一个临时的分支issue-001
来修复,但是假如此时我们正在dev
上进行工作,并且由于工作未完成,还无法提交,又要修复紧急的bug,怎么办?
此时我们可以使用Git的git stash
功能,git stash
会将当期工作现场保存下来,可以等以后恢复现场后继续工作:
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
可以使用git stash list查看保存的工作现场:
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
此时用git status
查看工作区,就是干净的,可以放心地创建分支来修复bug:
- 切换到
master
分支,创建临时bug分支issue-001
:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
(use "git push" to publish your local commits)
$ git checkout -b issue-101
Switched to a new branch 'issue-101'
- 修复bug,提交,合并,最后删除
issue-001
分支即可
$ git add readme.txt
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
(use "git push" to publish your local commits)
$ git merge --no-ff -m "merged bug fix 101" issue-001
Merge made by the 'recursive' strategy.
readme.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
修复完bug之后,即可恢复原来的工作现场了,可以使用两种方法来恢复:
git stash apply
,git stash apply
恢复现场后并不会把stash的内容删除,需要用git stash drop
来删除:
$ git stash apply {0}
git stash pop
,git stash pop
会在恢复现场的同时把stash的内容删除:
$ git stash pop
On branch dev
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: hello.py
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)
5.8 推送分支
本地分支修改完并且commit后,就可以把本地的代码分支修改推送到远程仓库中了。git push
命令用于将本地分支的修改推送到远程仓库。
git push命令的格式为:git push <remote> <branch>
其中:
<remote>
是远程仓库的名称,通常是origin
,但也可以是其他自定义名称。<branch>
是希望推送到远程仓库的分支名称。
git push命令常见用法与参数:
-
推送当前分支到远程仓库的同名分支:
git push origin
这个命令将当前分支的修改推送到远程仓库的同名分支。
-
推送指定分支到远程仓库的指定分支:
git push <remote> <local_branch>:<remote_branch>
这个命令将本地的
<local_branch>
分支推送到远程<remote>
仓库的<remote_branch>
分支上。
5.9 拉取分支
- git fetch
git fetch <remote> <branch>
git fetch
命令用于从远处仓库中获取最新的更改,但是不会将这些更改合并到当前分支中。
- git pull
git pull <remote> <branch>
git pull
命令从远程仓库中获取最新的更改,并将其自动合并到当前的分支中。实际上是git fetch
和git merge
两个命令的组合。
拉取远程分支,并建立本地分支
- 拉取远程分支:
git fetch origin feature_branch
- 基于远程分支创建本地分支:
git checkout -b feature_branch origin/feature_branch
或
git switch -c feature_branch origin/feature_branch # git版本较高才支持
这会创建一个本地分支,并且设置它跟踪相应的远程分支(upstream)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?