Git学习笔记
Posted FILWY_M
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Git学习笔记相关的知识,希望对你有一定的参考价值。
一、 初始化仓库(init)
# 不想要 git 管理跟踪的文件,可以在仓库根目录添加 .gitignore 文件,在里面写对应的规则
$ git init 把当前目录初始化为 git 仓库
Initialized empty Git repository in D:/学习研究/正在进行/Git学习/learngit/.git/
二、添加文件到仓库(add,commit)
Git管理的文件分为:工作区,版本库,版本库又分为暂存区stage和暂存区分支master(仓库)。
工作区(Working Directory): 比如我的learngit
文件夹就是一个工作区
版本库(Repository):工作区有一个隐藏目录.git
,这个不算工作区,而是 Git 的版本库。Git 的版本库里存了很多东西,其中最重要的就是称为 stage(或者叫 index)的暂存区,还有 Git 为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
前面讲了我们把文件往 Git 版本库里添加的时候,是分两步执行的:
第一步是用git add
把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建 Git 版本库时,Git 自动为我们创建了唯一一个master
分支,所以,现在,git commit
就是往master
分支上提交更改。
更多工作区和暂存区的概念,工作区和暂存区 - 廖雪峰的官方网站 (liaoxuefeng.com)。
# 创建一个readme.txt文件,并将该文件提交到暂存区,然后提交到仓库
$ git add readme.txt # 告诉 Git,把文件添加到仓库
# 添加许多同种类型的文件,可以使用通配符 * (记得加引号) 如: git add "*.txt" 命令就是添加所有 .txt 文件
# 使用git add . 则会将当前的所有文件都添加到仓库
$ git commit -m "wrote a readme file" # 告诉 Git,把文件提交到仓库
[master (root-commit) 4309065] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
$ git status # 查看仓库目前状态 (项目是否有修改、添加、未追踪的文件等)
On branch master
nothing to commit, working tree clean
三、 比较文件(diff)
-
git diff查看工作区和暂存区差异,
-
git diff --cached查看暂存区和仓库差异,
-
git diff HEAD 查看工作区和仓库的差异,
-
git add的反向命令git checkout,撤销工作区修改,即把暂存区最新版本转移到工作区,
-
git commit的反向命令git reset HEAD,就是把仓库最新版本转移到暂存区。
-
git diff 时是分为两种情况的:暂存区为空和暂存区不为空。
首先我们明确知道git diff是比较工作区和暂存区的文件的,如果此时暂存区为空,那么稍微有点不同,即:
-
暂存区为空使用git diff:因为此时暂存区为空,此时使用git diff同样也是比较工作区和仓库,即和使用git diff HEAD结果相同
-
暂存区不为空使用git diff:因为此时暂存区不为空,此时使用git diff比较的就是工作区和暂存区
讨论 - 廖雪峰的官方网站 (liaoxuefeng.com)
# 1. 修改文件某一文件之后,再查看仓库的状态
$ 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.txt
no changes added to commit (use "git add" and/or "git commit -a")
# 2. 查看修改内容, 查看文件不同 (difference)
$ git diff
diff --git a/readme.txt b/readme.txt
index d8036c1..013b5bc 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.
\\ No newline at end of file
# 3. 将修改后的文件添加到仓库
$ git add readme.txt
# 4. 查看当前状态
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
# 5. 提交到仓库
git commit -m "add distributed"
[master f06eedd] add distributed
1 file changed, 1 insertion(+), 1 deletion(-)
# 6. 再次查看当前仓库状态
$ git status
On branch master
nothing to commit, working tree clean
Changes to be committed:将要被提交的修改
四、提交记录和版本回退(log,reset)
HEAD 表示当前版本,也就是最新的提交
HEAD^ 上一个版本
HEAD^^ 上上一个版本
HEAD~100 往上100个版本
# 1. 查看版本提交记录
$ git log
commit ba9933f71761b488c284c5ddb9a2331276e4d7f8 (HEAD -> master)
Author: ********
Date: Mon Feb 21 12:47:00 2022 +0800
append GPL
commit f06eedd97fcbeff70c2a44bf79fe3eeedba97ff5
Author: ********
Date: Mon Feb 21 12:43:10 2022 +0800
add distributed
commit 430906517e1d196c45297edba787623e64acad9e
Author: ********
Date: Mon Feb 21 12:20:55 2022 +0800
wrote a readme file
# 2. 回退到上一个版本
$ git reset --hard HEAD^
HEAD is now at f06eedd add distributed
# 3. 再次查看版本提交记录
$ git log
commit f06eedd97fcbeff70c2a44bf79fe3eeedba97ff5 (HEAD -> master)
Author: ********
Date: Mon Feb 21 12:43:10 2022 +0800
add distributed
commit 430906517e1d196c45297edba787623e64acad9e
Author: ********
Date: Mon Feb 21 12:20:55 2022 +0800
wrote a readme file
最新的那个版本append GPL
已经看不到了!好比你从 21 世纪坐时光穿梭机来到了 19 世纪,想再回去已经回不去了,肿么办?
-
如果命令行窗口还没有被关掉:你就可以顺着往上找啊找啊,找到那个
append GPL
的commit id
是1094adb...
,于是就可以指定回到未来的某个版本:$ git reset --hard ba9933 HEAD is now at ba9933f append GPL
-
如果命令行窗口被关掉了:在 Git 中,总是有后悔药可以吃的。当你用
$ git reset --hard HEAD^
回退到add distributed
版本时,再想恢复到append GPL
,就必须找到append GPL
的 commit id。Git 提供了一个命令git reflog
用来记录你的每一次命令:$ git reflog f06eedd (HEAD -> master) HEAD@0: reset: moving to HEAD^ ba9933f HEAD@1: reset: moving to ba9933 f06eedd (HEAD -> master) HEAD@2: reset: moving to HEAD^ ba9933f HEAD@3: commit: append GPL f06eedd (HEAD -> master) HEAD@4: commit: add distributed 4309065 HEAD@5: commit (initial): wrote a readme file $ git reset --hard ba9933
五、撤销修改(checkout、reset)
-
撤销工作区中的修改
如果此时修改了文件,但是还没有提交到暂存区中。可以使用命令
git checkout -- file
来撤销修改。# 1. 查看文件内容(在最后一段添加了一行文字) $ cat readme.txt 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. My stupid boss still prefers SVN... # 2. 查看工作区状态 $ 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.txt no changes added to commit (use "git add" and/or "git commit -a") # 3. 撤销工作区的修改 $ git checkout -- readme.txt # 4. 查看当前工作区状态 $ git status On branch master nothing to commit, working tree clean # 5. 查看撤销修改后的内容(此时回到了修改之前的内容) $ cat readme.txt 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.
-
撤销暂存区中的修改
如果此时修改了文件,并且提交到暂存区中了。可以使用命令
git reset HEAD <file>
来把暂存区的修改撤销掉(unstage),重新放回工作区。git reset
命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD
时,表示最新的版本。# 1. 查看文件内容(在最后一段添加了一行文字) $ cat readme.txt 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. My stupid boss still prefers SVN... # 2. 添加文件到暂存区 $ git add readme.txt # 3. 查看当前工作区状态 $ git status On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: readme.txt # 4. 撤销暂存区中的修改 $ git reset HEAD readme.txt Unstaged changes after reset: M readme.txt # 5. 查看当前工作区状态 $ git reset HEAD readme.txt Unstaged changes after reset: M readme.txt
-
已经提交到版本库(仓库)中
使用上一章节的版本回退,可以回退到上一个版本,不过前提是没有推送到远程库。
六、删除文件(rm)
# 1. 添加一个新文件到暂存区
$ git add test.txt
# 2. 将暂存区的文件提交到版本库
$ git commit -m "add test.txt"
[master 160a0ae] add test.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 test.txt
# 3. 从工作区删除文件
$ rm test.txt
# 4. 查看当前状态
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
# 5. 从版本库中删除该文件
$ git rm test.txt
rm 'test.txt'
# 6. 执行commit
$ git commit -m "remove test.txt"
[master 930e953] remove test.txt
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 test.txt
# 7. 查看当前状态
$ git status
On branch master
nothing to commit, working tree clean
小提示:先手动删除文件,然后使用git rm <file>
和 git add<file >
效果是一样的。 注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!
命令git rm
用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
七、远程仓库(remote、push、clone)
在克隆或者推送仓库的时候可以选择SSH或者HTTPS协议,这两种方式的主要区别在于:
-
clone项目:使用ssh方式时,首先你必须是该项目的管理者或拥有者,并且需要配置个人的ssh key。而对于使用https方式来讲,就没有这些要求。
-
push:在使用ssh方式时,是不需要验证用户名和密码,如果你在配置ssh key时设置了密码,则仅需要验证配对密码。而对于使用https方式来讲,每次push都需要验证用户名和密码。
使用SSH方式
-
创建SSH Key。在用户主目录下,如果是windows路径在
C:\\Users\\Admin
,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:$ ssh-keygen -t rsa -C "youremail@example.com"
你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。如果一切顺利的话,可以在用户主目录里找到
.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件,这两个就是SSH Key的秘钥对,id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。 -
登陆GitHub,点击头像,选择Settings——SSH and GPG keys,然后,点 “New SSH Key”,填上任意 Title,在 Key 文本框里粘贴
id_rsa.pub
文件的内容,最终效果如下
1. 添加远程库
# 1. 查看远程地址
$ git remote -v
# 2. 添加远程地址
$ git remote add origin git@gitee.com:yiymou/learngit.git # 远程库的名字就是origin,是 Git 默认的叫法,也可以改成别的,
# 但是origin这个名字一看就知道是远程库。
# 3. 查看远程地址
$ git remote -v
origin git@gitee.com:yiymou/learngit.git (fetch)
origin git@gitee.com:yiymou/learngit.git (push)
# 4. push到远程仓库(origin)
$ git push -u origin master # 把当前分支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'.
# 5. 删除远程库地址
$ git remote rm origin
$ git push -u origin master
中的-u
参数是干嘛的?
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git 不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令,加了参数-u后,以后即可直接用git push 代替git push origin master。
2. 从远程克隆库
$ git clone git@gitee.com:yiymou/learngit.git
Cloning into 'learngit'...
remote: Enumerating objects: 28, done.
remote: Counting objects: 100% (28/28), done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 28 (delta 8), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (28/28), done.
Resolving deltas: 100% (8/8), done.
$ cd learngit/
$ ls
LICENSE.txt readme.txt test.txt
八、分支(branch、switch、checkout、merge)
分支管理 - 廖雪峰的官方网站 (liaoxuefeng.com)
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如 1.0 版本发布时,再把dev
分支合并到master
上,在master
分支发布 1.0 版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
1. 创建并合并分支
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了 50% 的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
如下图中圆圈表示的每一次的提交,每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长。
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
你看,Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
创建一个新的分支dev,并在dev分支中进行一次提交,然后再切换回到master分支,因为那个提交是在dev
分支上,而master
分支此刻的提交点并没有变:
# 1. 创建分支
$ git branch dev
# 2. 切换分支
$ git checkout dev
Switched to branch 'dev'
# 3. 创建并切换到新的分支(-b参数表示创建并切换,等同于上面两条命令结合。)
$ git checkout -b dev # 等同于命令git switch -c dev
Switched to a new branch 'dev'
# 4. 查看当前分支(当前分支前面会标一个*号)
$ git branch
# 5. 修改readme.txt文件
在最后一行添加
Creating a new branch is quick.
# 6. 在dev分支上正常提交
$ git add readme.txt
$ git commit -m "branch test"
[dev b17d20e] branch test
1 file changed, 1 insertion(+)
# 7. 切换回到master分支
$ git checkout master # 也可以使用命令git switch master
Switched to branch 'master'
# 8. 合并dev分支到当前分支(master分支)
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
# 9. 删除dev分支
$ git branch -d dev
Deleted branch dev (was 74c2f05).
# 10. 查看当前的分支(此时只剩下master分支)
$ git branch
* master
Fast-forward什么意思?
表示这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。当然,也不是每次合并都能Fast-forward
,后面还有其他的合并方式。
为什么要创建分支然后合并分支后又将分支删除?
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master
分支上工作效果是一样的,但过程更安全。
2. 解决冲突
设想一下一下情况,创建一个分支feature1
,在这个分支中修改readme
文件并提交,然后回到分支master
,此时同样修改readme
文件并提交,现在,master
分支和feature1
分支各自都分别有新的提交,变成了这样:
**这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。**此时就需要我们手动解决冲突后再提交。
# 1. 创建并切换到分支feature1
$ git switch -c feature1
Switched to a new branch 'feature1'
# 2. 修改readme.txt最后一行,改为
Creating a new branch is quick AND simple.
# 3. 在feature1分支上提交
$ git add readme.txt
$ git commit -m "AND simple"
[feature1 f4ca0f9] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)
# 4. 切换到master分支
$ git switch master
Switched to branch 'master'
# 5. 在master分支上把readme.txt文件的最后一行改为
Creating a new branch is quick & simple.
# 6. 在master分支上提交
$ git add readme.txt
$ git commit -m "& simple"
[master afc8945] & simple
1 file changed, 1 insertion(+), 1 deletion(-)
# 7. 这种情况下,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.
# 8. git status也可以告诉我们冲突的文件
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
# 9. 查看readme.txt的内容(Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容)
$ cat readme.txt
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.
My stupid boss still prefers SVN.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
# 10. 修改如下后保存
$ vim readme.txt
# 11. 在master分支上提交
$ git add readme.txt
$ git commit -m "conflict fixed"
[master 550cb7f] conflict fixed
# 11. 带参数git log可以看到分支的合并情况
$ git log --graph --pretty=oneline --abbrev-commit
* 550cb7f (HEAD -> master) conflict fixed
|\\
| * f4ca0f9 (feature1) AND simple
* | afc8945 & simple
|/
* 74c2f05 branch test
* 5859117 (origin-github/master, origin-gitee/master) add test.txt
* 930e953 remove test.txt
* 160a0ae add test.txt
* d1b9a9c git tracks changes
* 50859fa git tracks changes
* 7fc353e git tracks changes
* c595fa0 git tracks changes
* cc7e8f4 understand how stage works
* f06eedd add distributed
* 4309065 wrote a readme file
# 12. 删除feature1分支
$ git branch -d feature1
Deleted branch feature1 (was 14096d0).
在上面的例子中,我的理解是,当有冲突的时候,使用git merge此时会合并不成功,并且处于等待解决冲突的状态,同时会在有冲突的文件中使用用<<<<<<<,=======,>>>>>>>
标记出不同分支的内容,下一次提交后,git就会默认现在已经修改了冲突并将两个分支合并(不管此时有没有真正解决冲突,只要再次提交,git就认为冲突已经解决)。
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
用git log --graph
命令可以看到分支合并图。
并非更改了同一个文件就会发生冲突。
3. 制禁用Fast forward模式(–no-ff方式的
git merge)
通常,合并分支时,如果可能,Git 会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git 就会在 merge 时生成一个新的 commit,这样,从分支历史上就可以看出分支信息。
# 1. 创建并切换到分支dev
$ git switch -c dev
Switched to a new branch 'dev'
# 2. 在readme.txt最后一行增加
Creating a new branch is quick AND simple.
# 3. 在dev分支上提交
$ git commit -m "add a newline"
[dev f52c633] add merge
1 file changed, 1 insertion(+)
# 4. 切换回master
$ git switch master
Switched to branch 'master'
# 5. 使用--no-ff参数合并dev分支,表示禁用Fast forward
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
# 6. 用git log看看分支历史
$ git log --graph --pretty=oneline --abbrev-commit
* e1e9c68 (HEAD -> master) merge with no-ff
|\\
| * f52c633 (dev) add merge
|/
* cf810e4 conflict fixed
...
因为在使用合并–no-ff参数合并dev分支时要创建一个新的 commit,所以加上-m
参数,把 commit 描述写进去。不使用Fast forward
模式,merge 后就像这样:
4. Bug分支(stash)
设想这样一个情况,如果我们有两个分支,分别是master、dev。如果此时正在dev分支上工作,并且此时的共作还没有完成,所以还没有提交到仓库。此时如果有一个非常紧急的bug需要修复,此时那此时需要从dev分支切换到master分支,那么问题来了,如果我们此时直接切换过去,当前dev工作区进行的修改将会丢失,并且由于当前共作没有完成,所以不希望此时进行提交,此时还有一种解决方法,就是通过stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作。
在此时我们先使用stash功能保存当前的工作现场,然后切换到master分支,开始进行bug的修复,创建并切换到issue-101分支,解决bug,然后回到master分支,将issue-101分支合并到master分支,此时就解决了bug,然后我们需要继续进行dev分支的工作,此时切换到dev分支,因为刚刚的bug是在master分支上解决的,此时的dev分支上相同的bug还没有被修复,此时我们可以先使用Git提供的cherry-pick
命令,复制4c805e2 fix bug 101
这个提交所做的修改,然后使用git stash pop
,回到工作现场,继续进行工作。
# 1. 创建并切换到分支dev
$ git switch -c dev
Switched to a new branch 'dev'
# 2. 添加文件hello.py、修改文件readme.txt,查看当前状态
$ git status
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
# 3. 工作现场“储藏”起来
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
# 4. 切换到分支master,从master分支创建并切换到issue-101分支
$ git switch master
Switched to branch 'master'
$ git switch -c issue-101
Switched to a new branch 'issue-101'
# 5. 修复bug,需要把“Git is free software ...”改为“Git is a free software ...”,然后提交
$ git add readme.txt
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
# 5. 切换到master分支,并完成合并,最后删除issue-101分支
$ 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-101
Merge made by the 'recursive' strategy.
readme.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git branch -d issue-101
Deleted branch issue-101 (was 1057024).
# 6. 回到dev分支
$ git switch dev
Switched to branch 'dev'
$ git status
On branch dev
nothing to commit, working tree clean
# 7. 使用cherry-pick复制4c805e2 fix bug 101这个提交所做的修改
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
# 8. 使用git stash pop恢复工作现场
$ git stash pop
Auto-merging readme.txt
On branch dev
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: hello.py
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.txt
Dropped refs/stash@0 (4e26ea25447ee003f180d78a72c054be3763545f)
# 8. 使用git stash list查看所有保存过的工作现场(由于之前仅有的一个工作现场被pop了,所以现在的工作现场为空)
$ git stash list
5. 多人协作
一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?
master
分支是主分支,因此要时刻与远程同步;dev
分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;- bug 分支只用于在本地修复 bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个 bug;
- feature 分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
总之,就是在 Git 中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!
多人协作的工作模式通常是这样:
- 首先,可以试图用
git push origin <branch-name>
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并; - 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用
git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
$ git remote # 查看远程库信息
$ git remote -v # 查看远程信息(包括地址)
$ git remote add origin git@gitee.com:yiymou/learngit.git # 添加远程库地址
$ git remote rm origin # 删除已关联的远程库 origin
$ git push -u origin master # 把当前分支master推送到远程仓库(第一次)
$ git push origin master # 把当前分支master推送到远程仓库
$ git push origin dev # 把当前分支dev推送到远程仓库
$ git clone git@gitee.com:yiymou/learngit.git # 从远程克隆库
$ git pull origin develop # 从远程(origin) 的 develop 分支拉取代码
$ git branch --set-upstream branch-name origin/branch-name # 建立本地分支和远程分支的关联
九、git多账号设置
# 1. 查看远程地址
$ git remote -v
# 2. 添加远程地址
$ git remote add origin-gitee git@gitee.com:yiymou/learngit.git
$ git remote add origin-github git@github.com:YIYMOU/learngit.git
# 3. 查看远程地址
$ git remote -v
origin-gitee git@gitee.com:yiymou/learngit.git (fetch)
origin-gitee git@gitee.com:yiymou/learngit.git (push)
origin-github git@github.com:YIYMOU/learngit.git (fetch)
origin-github git@github.com:YIYMOU/learngit.git (push)
# 4. 分别push到远程仓库
$ git push origin-gitee master
Enumerating objects: 28, done.
Counting objects: 100% (28/28), done.
Delta compression using up to 8 threads
Compressing objects: 100% (24/24), done.
Writing objects: 100% (28/28), 2.35 KiB | 481.00 KiB/s, done.
Total 28 (delta 8), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.2]
To gitee.com:yiymou/learngit.git
* [new branch] master -> master
十、常用命令查阅
# 1. 仓库配置和初始化仓库
$ git config --global user.name "Your Name" # 配置所有 Git 仓库的 用户名 和 email
$ git config --global user.email "youremail@example.com" # 配置所有 Git 仓库的 用户名 和 email
$ git config user.name "Your Name" # 配置当前 Git 仓库的 用户名 和 email
$ git config user.email "youremail@example.com" # 配置当前 Git 仓库的 用户名 和 email
$ git config --global user.name # 查看全局配置的 用户名 和 email
$ git config --global user.email # 查看全局配置的 用户名 和 email
$ git config user.name # 查看当前仓库配置的 用户名 和 email
$ git config user.email # 查看当前仓库配置的 用户名 和 email
$ git init # 把当前目录初始化为 git 仓库
# 2. 添加文件到仓库
$ git add readme.txt # 告诉 Git,把文件添加到仓库
$ git commit -m "wrote a readme file" # 告诉 Git,把文件提交到仓库
$ git status # 查看仓库目前状态 (项目是否有修改、添加、未追踪的文件等)
# 3. 比较文件
$ git diff <file> # 查看修改内容, 查看文件不同 (difference)
$ git diff --cached
$ git diff HEAD -- <file>
# git diff 查看工作区(work dict)和暂存区(stage)的区别
# git diff --cached 查看暂存区(stage)和分支(master)的区别
# git diff HEAD -- <file> 查看工作区和版本库里面最新版本的区别
如: git diff readme.txt 表示查看 readme.txt 修改了什么,有什么不同
# 4. 提交记录和版本回退
HEAD 表示当前版本,也就是最新的提交
HEAD^ 上一个版本
HEAD^^ 上上一个版本
HEAD~100 往上100个版本
$ git log # 查看提交记录
$ git log --graph # 查看分支合并图
$ git log --graph --pretty=oneline --abbrev-commit
$ git log --oneline #美化输出信息,每个记录显示为一行,显示 commit_id 前几位数
$ git log --pretty=oneline #美化输出信息,每个记录显示为一行,显示完整的 commit_id
$ git log --graph --pretty=format:'%h -%d %s (%cr)' --abbrev-commit --
$ git reset --hard HEAD^ # 回退到上一个版本
$ git reflog # 查看版本改变记录
$ git reset --hard <commit_id> # 通过commit_id来指定要回退的版本
# 5. 撤销修改
$ git checkout -- readme.txt # 撤销工作区的修改
$ git restore readme.txt # 撤销工作区的修改
$ git reset HEAD readme.txt # 撤销暂存区中的修改
$git resotre --worktree readme.txt # 从暂存区恢复工作区,
$git restore --staged readme.txt # 从master恢复暂存区
$git restore --source=HEAD --staged --worktree readme.txt # 从master同时恢复工作区和暂存区
# 6. 删除文件
$ rm test.txt # 从工作区中删除文件
$ git rm test.txt # 从版本库中删除文件
# 7. 远程仓库
$ git remote # 查看远程库信息
$ git remote -v # 查看远程地址
$ git remote add origin git@gitee.com:yiymou/learngit.git # 添加远程库地址
$ git remote rm origin # 删除已关联的远程库 origin
$ git push -u origin master # 把当前分支master推送到远程
$ git clone git@gitee.com:yiymou/learngit.git # 从远程克隆库
$ git pull origin develop # 从远程(origin) 的 develop 分支拉取代码
$ git branch # 查看分支
$ git branch dev # 创建分支dev
$ git checkout <name> # 切换分支
$ git switch <name> # 切换分支
$ git checkout -b <name> # 创建+切换分支
$ git switch -c <name> # 创建+切换分支
$ git merge <name> # 合并某分支到当前分支
$ git merge --no-ff -m "merge with no-ff" dev # 合并 dev 分支到当前分支(禁用Fast forward 合并策略)
$ git branch -d <name> # 删除分支
$ git branch -D dev # 强制删除 dev 分支
$ git stash # 保存当前工作区和暂存区的修改状态,切换到其他分支修复 bug 等工作,然后在回来继续工作
$ git stash list # 查看保存现场的列表
$ git stash pop # 恢复的同时把 stash 内容也删除
$ git stash apply # 恢复现场,stash内容并不删除
$ git stash drop # 删除 stash 内容
$ git stash apply stash@0 # 多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash
$ git cherry-pick <commit> # 复制一个特定的提交到当前分支(当前分支的内容需要先 commit,然后冲突的文件需要解决冲突,然后 commit)
$ git rebase # 把本地未push的分叉提交历史整理成直线(使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比)
# 其他
$ git 查看 git 的相关命令 (git --help)
$ git --version 查看 git 的版本
$ git config 查看 git config 的相关命令
$ git config core.autocrlf # 查看core.autocrlf配置状态
$ git config --global core.autocrlf false # 配置core.autocrlf状态为false/input/true
# Linux通用命令
$ clear # 清屏
$ mkdir learngit # 创建目录
$ cd learngit # 改变目录
$ pwd # 用于显示当前目录
$ ls -ah # 查看当前目录下的文件,包含隐藏文件 (不带 -ah 看不了隐藏文件)
$ cat readme.txt # 查看文本文件
$ rm test.txt # 删除文件
十一、补充
所有的版本控制系统,其实只能跟踪文本文件的改动,比如 TXT 文件,网页,所有的程序代码等等,Git 也不例外。版本控制系统可以告诉你每次的改动,比如在第 5 行加了一个单词 “Linux”,在第 8 行删了一个单词 “Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从 100KB 改成了 120KB,但到底改了啥,版本控制系统不知道,也没法知道。
千万不要使用 Windows 自带的记事本编辑任何文本文件。原因是 Microsoft 开发记事本的团队使用了一个非常弱智的行为来保存 UTF-8 编码的文件,他们自作聪明地在每个文件开头添加了 0xefbbbf(十六进制)的字符,你会遇到很多不可思议的问题,比如,网页第一行可能会显示一个 “?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。建议你下载 Visual Studio Code 代替记事本,不但功能强大,而且免费!
为什么 Git 比其他版本控制系统设计得优秀,因为 Git 跟踪并管理的是修改,而非文件。
十二、常见问题
-
git add时遇到错误
warning: LF will be replaced by CRLF in test.txt. The file will have its original line endings in your working directory
首先问题出在不同操作系统所使用的换行符是不一样的
Uinx/Linux采用换行符LF表示下一行(LF:LineFeed,中文意思是换行);
Windows采用回车+换行CRLF表示下一行(CRLF:CarriageReturn LineFeed,中文意思是回车换行);
Mac OS采用回车CR表示下一行(CR:CarriageReturn,中文意思是回车)。
假如你正在Windows上写程序,又或者你正在和其他人合作,他们在Windows上编程,而你却在其他系统上,在这些情况下,你可能会遇到行尾结束符问题。这是因为Windows使用回车和换行两个字符来结束一行,而Mac和Linux只使用换行一个字符。虽然这是小问题,但它会极大地扰乱跨平台协作。
使用命令查看配置状态
git config core.autocrlf
得到的结果可能为input,true,false,分别表示
true add时git会把CRLF转换成LF false 保持不变 input add时git会把CRLF转换成为LF,签出时依旧为LF -
git push时遇到的错误
$ git push origin-gitee master The authenticity of host 'gitee.com (212.64.62.183)' can't be established. ED25519 key fingerprint is SHA256:+ULzij2u99B9eWYFTw1Q4ErYG/aepHLbu96PAUCoV88. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])?
文件夹内少了一个known_hosts文件,本来密钥文件应该是三个,现在只有两个,便报了这样的错误,此时输入yes回车之后,生成了缺少了的known_hosts文件,便可解决这个问题。
-
删除分支时遇到的错误
$ git branch -d issue-101 error: The branch 'issue-101' is not fully merged. If you are sure you want to delete it, run 'git branch -D issue-101'.
问题背景:现在我有三个分支,分别是 master、dev、issue-101。 在issue-101分支中进行了修改,然后切换到master分支,将issue-101合并到了master分支中,此时切换到dev分支,尝试在dev分支中删除issue-101分支,就会出先上面遇到的错误。
原因:将要删除的分支还没有合并到当前所在的分支。
解决方法:切换到master分支中,在master分支中删除issue-101分支,因为issue-101分支已经合并到了master分支中,所以可以进行删除。如果想要在dev分支中删除issue-101分支中,可以使用强制删除
git branch -D issue-101
。
参考文献
Git教程 - 廖雪峰的官方网站 (liaoxuefeng.com)
git两种连接方式:ssh和http配置介绍 - 舞羊 - 博客园 (cnblogs.com)
Git:SSH、SSH与HTTP区别、git常用命令_Code farmer Aiden-CSDN博客_ssh和http
以上是关于Git学习笔记的主要内容,如果未能解决你的问题,请参考以下文章