前言

这是基于acwing中linux系统课下的git网课写的笔记。

由于自己没系统学过就直接上手一边用一边现查然后发现玩不下去,所以转而系统学习,课前提要的时候y总也说他学习的时候也是这样的感受,所以让我们不要像学vim、tmux和ssh那样像学工具一样学习,一定要先系统地学完再使用git命令。

由于git命令实在过于复杂(我认为倒也不是命令复杂,而是里边的逻辑概念很难形成一个清晰的认知,哪怕已经有点概念了,只要不是相当明确,就会出现同样的需求前一次感觉已经谈不上稀里糊涂有点概念地实现成功了,重新再实现一遍居然又遇上新的报错),我写了此篇笔记,用来之后翻看用。

acwing的讲义

acwing有提供过一套讲义,我先将它复制到此处。

git基本概念

  • 工作区:仓库的目录。工作区是独立于各个分支的。
  • 暂存区:数据暂时存放的区域,类似于工作区写入版本库前的缓存区。暂存区是独立于各个分支的。
  • 版本库:存放所有已经提交到本地仓库的代码版本
  • 版本结构:树结构,树中每个节点代表一个代码版本。

git常用命令

  • git config --global user.name xxx:设置全局用户名,信息记录在~/.gitconfig文件中
  • git config --global user.email xxx@xxx.com:设置全局邮箱地址,信息记录在~/.gitconfig文件中
  • git init:将当前目录配置成git仓库,信息记录在隐藏的.git文件夹中
  • git add XX:将XX文件添加到暂存区
    • git add .:将所有待加入暂存区的文件加入暂存区
  • git rm --cached XX:将文件从仓库索引目录中删掉
  • git commit -m “给自己看的备注信息”:将暂存区的内容提交到当前分支
  • git status:查看仓库状态
  • git diff XX:查看XX文件相对于暂存区修改了哪些内容
  • git log:查看当前分支的所有版本
  • git reflog:查看HEAD指针的移动历史(包括被回滚的版本)
  • git reset --hard HEAD^git reset --hard HEAD~:将代码库回滚到上一个版本
    • git reset --hard HEAD^^:往上回滚两次,以此类推
    • git reset --hard HEAD~100:往上回滚100个版本
    • git reset --hard 版本号:回滚到某一特定版本
  • git checkout — XXgit restore XX:将XX文件尚未加入暂存区的修改全部撤销
  • git remote add origin git@git.acwing.com:xxx/XXX.git:将本地仓库关联到远程仓库
  • git push -u (第一次需要-u以后不需要):将当前分支推送到远程仓库
  • git push origin branch_name:将本地的某个分支推送到远程仓库
  • git clone git@git.acwing.com:xxx/XXX.git:将远程仓库XXX下载到当前目录下
  • git checkout -b branch_name:创建并切换到branch_name这个分支
  • git branch:查看所有分支和当前所处分支
  • git checkout branch_name:切换到branch_name这个分支
  • git merge branch_name:将分支branch_name合并到当前分支上
  • git branch -d branch_name:删除本地仓库的branch_name分支
  • git branch branch_name:创建新分支
  • git push --set-upstream origin branch_name:设置本地的branch_name分支对应远程仓库的branch_name分支
  • git push -d origin branch_name:删除远程仓库的branch_name分支
  • git pull:将远程仓库的当前分支与本地仓库的当前分支合并
    • git pull origin branch_name:将远程仓库的branch_name分支与本地仓库的当前分支合并
  • git branch --set-upstream-to=origin/branch_name1 branch_name2:将远程的branch_name1分支与本地的branch_name2分支对应
  • git checkout -t origin/branch_name 将远程的branch_name分支拉取到本地
  • git stash:将工作区和暂存区中尚未提交的修改存入栈中
  • git stash apply:将栈顶存储的修改恢复到当前分支,但不删除栈顶元素
  • git stash drop:删除栈顶存储的修改
  • git stash pop:将栈顶存储的修改恢复到当前分支,同时删除栈顶元素
  • git stash list:查看栈中所有元素

acwing开发的仿github平台

网址:git.acwing.com
y总说马云的gitee广告太多,不喜欢,所以自己开发了这个。
这个平台可以国内网进且快一点,且目前基本只有acwing的同学知道,氛围会好一点。
y总说了,是免费的,大家也可以拿这个替代github。
我去看了下,前端界面开发得挺好看的。
(但是好像终端得用AC terminal,而AC terminal只有买了课的同学才能用,且给每个人分的带宽不多,亲测是很卡的。)

网课笔记

树模型

  • git其实相当于树模型,HEAD其实类似于头指针。
    因为这是个树模型,所以HEAD也可以在不同分支间切换。
  • 仓库信息在.git文件夹内
    1
    2
    3
    4
    5
    6
    7
    8
    9
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ ls -a
    . .git CONTERIBUTING.md HelloWorld db.sqlite3 statics templatetags
    .. .vscode CONTRIBUTING.md TestModel manage.py templates
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ cd .git
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet/.git$ ls -a
    . COMMIT_EDITMSG HEAD branches description index logs packed-refs
    .. FETCH_HEAD ORIG_HEAD config hooks info objects refs
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet/.git$ cat HEAD
    ref: refs/heads/Lucy1

版本

  • git status可以用来查看当前是否有文件没加进来,比如我给文件夹里添加一个111.html.
    可以对比一下git add .之前和之后的git status给的提示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git status
    On branch Lucy1
    Untracked files:
    (use "git add <file>..." to include in what will be committed)
    templates/111.html

    nothing added to commit but untracked files present (use "git add" to track)
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git add .
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git status
    On branch Lucy1
    Changes to be committed:
    (use "git restore --staged <file>..." to unstage)
    new file: templates/111.html

  • git commit -m "给自己或合作者看的备注信息"记得要加-m!!!
    git commit -m "给自己或合作者看的备注信息"命令之后,将我们当前暂存区的版本存到了我们当前分支的下一个节点,并将HEAD指向这个点,并且这个节点的记录存下备注信息。

  • 注意可以使用git diff XX查看XX文件相对于暂存区修改了哪些内容

  • 关于git rm --cached XXgit restore --stage XX:这俩都可以从暂存区里撤回点东西,区别是,rm是直接删,而restore是回滚。

  • git log是从最初的空节点沿着当前分支走到当前节点,只有这条路径的log。
    所展示的内容,放最上面的是最新修改,越往下越旧,直到走到空节点。

    • 有个命令叫git log --pretty=oneline,这样每个节点只展示一行。
      部分log展示如下(只包含最近的几条信息,先前的太多了就不展示了):
      1
      2
      3
      4
      5
      zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet/templates$ git log --pretty=one
      line
      79c2e08e07bf9f7916544c8641efee252b66b420 (HEAD -> Lucy1) add 111.html
      80bab04e71ae3ec61af0cf46ba58cdfee8cc6877 delete 111.html
      6024a913bf3557b9423a832f7a3d50a08d1e0320 add 111.html
  • 往前回滚版本

    • git reset --hard HEAD^一个^代表回滚一个版本,两个^代表回滚两个版本,依此类推。
      连续执行两次git reset --hard HEAD^命令的效果等同于git reset --hard HEAD^^,而不等同于回滚回去再滚回来。

      • 回滚完之后,HEAD指针会前移,而git log只能显示根节点到HEAD的路径。虽然这里的回滚,不会将之前的内容删掉,但是git log已经看不到回滚前的了,那么如何回退回来呢?
      • git reflog命令展示HEAD的移动记录。
        1
        2
        3
        4
        5
        zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git reflog
        80bab04 (HEAD -> Lucy1) HEAD@{0}: reset: moving to HEAD^
        79c2e08 HEAD@{1}: commit: add 111.html
        80bab04 (HEAD -> Lucy1) HEAD@{2}: commit: delete 111.html
        6024a91 HEAD@{3}: commit: add 111.html
        (部分reflog展示)
      • 在reflog里可以看到每个版本的编号,这个编号是hash值的前7位,这里的版本号用来跳转到任意一个节点(因为所有创建过的目录,HEAD一定是走过的,所以一定会在reflog里出现过)。
        1
        2
        3
        4
        5
        6
        7
        zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git reflog
        79c2e08 (HEAD -> Lucy1) HEAD@{0}: reset: moving to 79c2e08
        80bab04 HEAD@{1}: reset: moving to 80bab04
        80bab04 HEAD@{2}: reset: moving to HEAD^
        79c2e08 (HEAD -> Lucy1) HEAD@{3}: commit: add 111.html
        80bab04 HEAD@{4}: commit: delete 111.html
        6024a91 HEAD@{5}: commit: add 111.html
        (部分reflog展示)
        这里可以关注到,回滚回某个版本的时候,版本号和回滚的版本号一致,比如这里出现了两个79c2e08和两个80bab04。
      • 关于git reset --hard HEAD^^
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git log
        commit 79c2e08e07bf9f7916544c8641efee252b66b420 (HEAD -> Lucy1)
        Author: zstu21 <3435657471@qq.com>
        Date: Wed Dec 13 23:46:55 2023 +0800

        add 111.html

        commit 80bab04e71ae3ec61af0cf46ba58cdfee8cc6877
        Author: zstu21 <3435657471@qq.com>
        Date: Wed Dec 13 23:21:54 2023 +0800

        delete 111.html

        commit 6024a913bf3557b9423a832f7a3d50a08d1e0320
        Author: zstu21 <3435657471@qq.com>
        Date: Wed Dec 13 23:18:21 2023 +0800

        add 111.html
        # (此处省略前边历史)
        zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git reset --hard HEAD^^
        HEAD is now at 6024a91 add 111.html
        zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git reset --hard HEAD^^
        HEAD is now at 6024a91 add 111.html
        zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git reflog
        6024a91 (HEAD -> Lucy1) HEAD@{0}: reset: moving to HEAD^^
        79c2e08 HEAD@{1}: reset: moving to 79c2e08
        80bab04 HEAD@{2}: reset: moving to 80bab04
        80bab04 HEAD@{3}: reset: moving to HEAD^
        79c2e08 HEAD@{4}: commit: add 111.html
        80bab04 HEAD@{5}: commit: delete 111.html
        6024a91 (HEAD -> Lucy1) HEAD@{6}: commit: add 111.html
        # (此处省略前边历史)
        可以发现这里的HEAD^^不是回滚reflog的前两个版本,而是回滚git log的前两个版本(git log是沿着当前分支从根节点走到当前HEAD节点路径的节点)。
    • 返回到上次保存(返回目前暂存区的版本,不是返回暂存区上一次的版本,就比如我往111.html中添加了111这句话,没git add .,那我git restore .后就是把111这行话删除,回滚到111.html没有111这句话的状态)的版本:

      • git restore .:全部文件返回上次保存的版本。
      • git restore XX:XX文件返回上次保存的版本。
  • git add XX也可以将删除XX文件的记录添加到暂存区内。

    1
    2
    3
    4
    5
    6
    7
    8
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet/templates$ rm 111.html # linux命令
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet/templates$ git add 111.html
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet/templates$ git status
    On branch Lucy1
    Changes to be committed:
    (use "git restore --staged <file>..." to unstage)
    deleted: 111.html

  • git add XX YY这样并排地写两个文件,意为把XX和YY两个文件的信息都添加到暂存区。

  • 这里若是误删,可以用git restore XX恢复XX文件(相当于从回收站还原文件)。

这里需要注意的是,git loggit reflog记录的版本都只有持久化后的版本的,只有git commit -m "XX"后才会生成一个新版本。
暂存区无变化的情况下,用git commit -m "XX"命令多次的结果是,无论在git log还是git reflog中,都不会生成新的版本,只有第一次commit的记录。

目前都是对本地的操作,跟云端没有关系。

github上查看历史版本

现在开始讲云端

github上绿色的<>code按钮下边有个n commits的按钮,点击就可以查看每个分支的commits

  • 使用git reset --hard [你的commit id]可以恢复历史版本,然后再用git push命令推送到远程仓库。

  • 这里的commit id是版本号中提交到远程仓库的部分,比起git reflog命令显示的版本号,缺少回滚部分。

  • git reflog中(我目前所看到的只有三种,说明这三种最常见):

    1
    2
    3
    6024a91 HEAD@{8}: checkout: moving from branch1 to branch2
    731779f HEAD@{16}: commit: delete 111.html
    6024a91 HEAD@{17}: reset: moving to HEAD^^

    分别为checkout,commit和reset,其中github仓库commit id中,checkout部分也没有。因为github仓库的commits记录是按分支分类的,没必要把本地切换分支的记录也写上去,要写也不知道写哪去。
    github仓库的commit记录是push上去的记录,而且git commit -m "XX"只能使用两次,再使用一次会不成功并提示要git push一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git commit -m "delete 111.cpp"
    On branch hzh11
    Your branch is ahead of 'origin/hzh11' by 2 commits.
    (use "git push" to publish your local commits)

    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: 111.cpp

    no changes added to commit (use "git add" and/or "git commit -a")

    这里告诉你已经有两个commits了,不能再多了,要想再commit就要先push一下,下面部分就是你没commit成功所以有这些东西没有存。

branch

  • (在本地)创建新分支的命令是git checkout -b branchname
    创建分支是从当前HEAD中继承出来的(内容继承自当前HEAD分支),但是只是有个趋势,不会创建一个新的节点,用git log命令可以发现什么都没发生,但是用git reflog命令,会出现一个新记录。

  • 创建新分支,会创完并跳转到这个分支:

    1
    2
    zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git checkout -b branch1
    Switched to a new branch 'branch1'

注意暂存区和分支没有关系,跟分支是独立的。不管哪个分支都共用了一个暂存区,commit的时候,会看你当前在哪个分支,就把暂存区内容加到哪个分支的后面。切换分支的时候,不会有多个暂存区,不管在哪个分支上都用了一个暂存区和工作目录。

  • git branch 命令:
    分支操作 命令
    创建分支 git branch <name>:创建叫name的分支,但仍然停留在当前分支。
    git checkout -b <name>:创建叫name的分支,并切换到name分支。
    删除分支 git branch -d <name>:参数为-D则为强制删除,这个命令是删除本地分支。
    git push origin --delete <name>:删除远程仓库的叫name的分支,同名的本地分支并不会被删除,所以还需要单独删除本地同名分支。
    git branch -dr <remote>/<branch-name>:没有删除远程分支,只是删除 git branch -r 列表中的追踪分支。一般只有 git push 命令可以修改远程仓库。
    创建+切换分支 git switch <name>:只能用来切换到已有的分支,不能用来创建新的分支。
    git checkout <name>:只能用来切换到已有的分支,不能用来创建新的分支。
    git branch <name>:只能用来创建新分支,不能用来切换分支。创建但不会切换到新分支。
    git switch -c <name>:只能用来创建新分支,不能用来切换分支。创建但切换到新分支。
    查看分支 git branch:查看本地分支,当前分支前面会标一个*号。
    git branch -r:查看远程分支和本地分支的关联。
    git branch -a:查看本地分支和本地分支与远程分支的关联,本地分支与远程分支的关联会用红色表示出来(如果你开了颜色支持的话)。
    git branch -v:查看每个分支的最新版本和版本号。
    git branch -vv:类似于 git branch -v,区别在于会有蓝色提示,类似于 [origin/branch_name],表示最新commit的修改在这个分支。
    重命名分支 git branch -m oldName newName
    操作远程分支 git push --set-upstream origin branch_name:设置本地的branch_name分支对应远程仓库的branch_name分支。
    git push -u origin/remote_branch_name:将本地新建的分支与远程分支相关联,同上。
    git branch --unset-upstream:撤销当前本地分支和其远程分支的关联。
    git push -d origin branch_name:删除远程仓库的branch_name分支。
    git push origin branch_name:将本地的某个分支推送到远程仓库。
    • git merge branch_name:将分支branch_name合并到当前分支上
      如果无冲突就是fast-forward合并了。
      如果有冲突,比如在同一个文件上,两个分支的内容不一样,就会提示合并不成功(但是你当前所在的文件会出现一些“》》》》》》”类的东西),请手动合并后再持久化:

      1
      2
      3
      4
      zstueto@LAPTOP-QJGKA05C:/mnt/d/Internet$ git merge test1
      CONFLICT (add/add): Merge conflict in 1.cpp
      Auto-merging 1.cpp
      Automatic merge failed; fix conflicts and then commit the result.

      对于有冲突的文件,打开,用vsc的合并编辑器或者linux终端的vim或者纯手动修改后,commit一下就行。
      另外,若想禁用fast-forward模式,用–no-ff,这个有需求的时候自己可以临时查。

    • git用远程分支代码强制覆盖本地分支代码:

      1
      2
      3
      git fetch --all
      git reset --hard origin/master
      git pull
    • git用本地代码强制覆盖远程代码:

      1
      git push origin branch_name -f

    栈空间

    • git stash用来:
      有时,当你在项目的一部分上已经工作一段时间后,所有东西都进入了混乱的状态, 而这时你想要切换到另一个分支做一点别的事情。 问题是,你不想仅仅因为过会儿回到这一点而为做了一半的工作创建一次提交。 针对这个问题的答案是 git stash 命令。
    • git stash:将工作区和暂存区中尚未提交的修改存入栈中
    • git stash apply:将栈顶存储的修改恢复到当前分支,但不删除栈顶元素
    • git stash drop:删除栈顶存储的修改
    • git stash pop:将栈顶存储的修改恢复到当前分支,同时删除栈顶元素
    • git stash list:查看栈中所有元素

    可以看这个教程 Git 工具 - 贮藏与清理