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 GPLcommit id1094adb...,于是就可以指定回到未来的某个版本:

    $ 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)

  1. 撤销工作区中的修改

    如果此时修改了文件,但是还没有提交到暂存区中。可以使用命令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.
    
  2. 撤销暂存区中的修改

    如果此时修改了文件,并且提交到暂存区中了。可以使用命令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
    
    
  3. 已经提交到版本库(仓库)中

    使用上一章节的版本回退,可以回退到上一个版本,不过前提是没有推送到远程库。

六、删除文件(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方式

  1. 创建SSH Key。在用户主目录下,如果是windows路径在C:\\Users\\Admin,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsaid_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

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

    你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsaid_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

  2. 登陆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 中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!

多人协作的工作模式通常是这样:

  1. 首先,可以试图用git push origin <branch-name>推送自己的修改;
  2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
  3. 如果合并有冲突,则解决冲突,并在本地提交;
  4. 没有冲突或者解决掉冲突后,再用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 跟踪并管理的是修改,而非文件。

十二、常见问题

  1. 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,分别表示

    trueadd时git会把CRLF转换成LF
    false保持不变
    inputadd时git会把CRLF转换成为LF,签出时依旧为LF

    可以参考该文章git warning: LF will be replaced by CRLF in XXXX.The file will have its original line endings in …_qi_sheng_的博客-CSDN博客

  2. 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文件,便可解决这个问题。

  3. 删除分支时遇到的错误

    $ 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 warning: LF will be replaced by CRLF in XXXX.The file will have its original line endings in …_qi_sheng_的博客-CSDN博客

以上是关于Git学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

git 学习笔记总结

Git与GitHub学习笔记使用 Github Pages 管理项目文档

Git学习笔记3

git 基础学习笔记

Git 学习笔记

Git学习笔记——Git安装