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版本:可以直接通过源码安装,从官网下载源码,然后解压,依次输入:./configmakesudo 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秘钥

  1. 创建SSH Key

在Git bash中执行以下命令:

$ ssh-keygen -t rsa -C "youremail@example.com"

之后将会在用户的主目录下,生成.ssh目录,目录下有id_rsaid_pub文件

  1. 登录GitHub

打开"Account settings","SSH Keys"页面,点击"Add SSH Key",填上任意Title,在Key文本框里粘贴id_rsa.pub文件中的内容:

github添加ssh keys

4.2 添加远程仓库

目前,已经在本地有了一个Git仓库,可以在GitHub上再创建一个远程Git仓库,并且让这两个仓库远程同步。

  1. 登录GitHub,点击"Create a new repo",创建一个新的仓库:

git创建仓库

  1. 目前GitHub上的这个新建的仓库还是空的,可以通过从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后把本地仓库的内容推送到GitHub仓库:

在本地的learngit仓库下运行以下命令:

$ git remote add origin git@github.com:<username>/learngit.git  # 填上自己的用户名

远程仓库的名称默认就叫origin

  1. 把本地库的所有内容推送到远程库上:
$ 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,就能确定当前分支,以及当前分支的提交点:

git分支管理原理1

每次提交,master分支都会向前移动一步,随着开发者不断提交,master分支的线也越来越长。

  • 当创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

git分支管理原理2

  • 创建dev分支后,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

git分支管理原理3

  • 假如在dev上的工作完成了,就可以把dev合并到master上,Git合并就是把master直接指向dev的当前提交:

git分支管理原理4

  • 合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,就剩下了一条master分支:

git分支管理原理5

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 applygit stash apply恢复现场后并不会把stash的内容删除,需要用git stash drop来删除:
$ git stash apply {0}
  • git stash popgit 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命令常见用法与参数:

  1. 推送当前分支到远程仓库的同名分支:

    git push origin
    

    这个命令将当前分支的修改推送到远程仓库的同名分支。

  2. 推送指定分支到远程仓库的指定分支:

    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 fetchgit merge两个命令的组合。

拉取远程分支,并建立本地分支

  1. 拉取远程分支:
git fetch origin feature_branch
  1. 基于远程分支创建本地分支:
git checkout -b feature_branch origin/feature_branch

git switch -c feature_branch origin/feature_branch  # git版本较高才支持

这会创建一个本地分支,并且设置它跟踪相应的远程分支(upstream)。

posted @   N1rv2na  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示