Git 入门速览

2,383 阅读3分钟

资料援引

知乎:git 合并的时候如何只合并部分文件?

廖雪峰:史上最浅显易懂的Git教程!

工作区和暂存区

  • 工作区(Working Directory :在电脑里能看到的目录。
  • 版本库(Repository) :工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
  • 暂存区Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区。

撤销修改

还未 add

# 假设修改了 readme.txt 文件,但还没有 git add,此时通过 git status 命令来看看情况
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  # 廖雪峰老师的文章里git提示的撤销命令如下:
  (use "git checkout -- <file>..." to discard changes in working directory)
  # 但实践时我的git提示的撤销命令如下:
  (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")



# 现在丢弃工作区的修改
# 这里 <file> 之前的 -- 很重要,缺少的话命令就变成了“切换到另一个分支”
git checkout -- readme.txt
# restore 命令功能同上
git restore readme.txt

这里有两种情况:

  • 一种是<file>自修改后还没有被放到暂存区,撤销修改就回到和版本库相同的状态;
  • 一种是<file>已经添加到暂存区后,又作了修改,撤销修改就回到添加到暂存区后的状态。

总之,就是让这个文件回到最近一次git commitgit add时的状态。

已经 add 但没有 commit

# 假设修改了 readme.txt 文件,且执行了 git add,此时通过 git status 命令来看看情况
$ git status
On branch master
Changes to be committed:
    # 廖雪峰老师的文章里git提示的撤销命令如下:
  (use "git reset HEAD <file>..." to unstage)
  # 但实践时我的git提示的撤销命令如下:
  (use "git restore --staged <file>..." to unstage)

        modified:   readme.txt
        
# 现在丢弃暂存区的修改
$ git reset HEAD readme.txt
Unstaged changes after reset:
M        readme.txt
# restore 命令效果同上
git restore --staged readme.txt

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

已经 commit 但没有 push

# 假设修改了 readme.txt 文件,且执行了 git add、git commit
# 此时通过 git status 命令来看看情况
$ git status 
On branch <branch name>
nothing to commit, working tree clean

# 现在回退 commit 版本
# 1. 查看提交历史
git log
# 2. 从当前(HEAD)回退到某个之前的 commit_id
git reset --hard commit_id
# 3. 也可以查看命令历史,从而撤销第二步的回退
git reflog
# 4. 从 commit_id 回到 HEAD
git reset --hard HEAD

--hard外,可选的参数还有--soft

  • –-soft:回退到某个版本,只回退了commit的信息(只改变了HEAD指针的指向),不会清空暂存区(不会改变代码)。如果还要提交,直接commit即可;

  • -–hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,撤销的commit中所包含的更改被抹除掉,整个暂存区干净。

已经 push

# 1. 查看提交历史
git log
# 2. 从当前(HEAD)回退到某个之前的 commit_id,也可以用 --soft,自行选择。
git reset --hard commit_id
# 3. --force 强制提交当前版本
git push origin <branch name> -f

分支管理

切换到指定版本

git checkout <commit id>

git checkout本质上是用版本库里的版本替换工作区的版本,因此无论是修改还是删除工作区的内容,都可以“一键还原”。

创建并切换分支

git checkout -b <branch name>

# 相当于以下两条命令
git branch <branch name>
git checkout <branch name>

git checkout又是用来撤销修改,又是用来(创建)切换分支。同一个命令,有两种作用,有点令人迷惑。

实际上,切换分支这个动作,用switch更合理。

// 创建并切换分支
git switch -c <branch name>
// 切换到已有分支
git switch <branch name>

删除本地分支

git branch -d <branch name>
// 将 -d 改为 -D 可强制删除

查看当前分支

$ git branch
* dev // 当前分支前会有个 “*”
  master

查看当前分支的父分支

// 严格来讲该命令是用来看 <branch name> 的操作记录的
// 只是可以通过在操作记录中寻找创建分支的命令,从而得知父分支是哪个
git reflog show <branch name>

合并/部分合并

git merge 合并

分支合并,将A分支合并到B分支。

// 切换到A分支
git checkout A

// 获取A分支最新代码
git pull

// 切换到B分支
git checkout B

// 获取B分支最新代码
git pull

// 合并分支
git merge A

合并有几种模式:

  1. Fast-forward:快进模式,也就是直接把B指向A的当前提交,所以合并速度非常快。这种情况是因为从B上B1节点生成A分支后,只在A分支上有过提交,而直到将A合并到B,B仍处于B1节点,这等价于一直就在B分支上做修改,A1节点即是B1节点,An节点即是Bn节点。
  2. 普通模式:B1节点生成A分支后,B和A都有新的操作,但两个分支的操作不涉及相同文件的修改,此时合并速度虽然不如快进模式快,但是不会引起冲突。
  3. 合并冲突:这是因为在两个分支上对同一个文件有了不同的操作导致的,通常分歧会以下面这种形式体现:
<<<<<<< HEAD
B 的操作
=======
A 的操作
>>>>>>> A

这种情况下需要手动修复冲突,再完成合并操作。

合并指定的文件

有时候想要合并A分支指定的文件或者文件夹到B分支上去,例如合并A分支的README.md文件到B分支上面。

// 切换到A分支
git checkout A

// 获取A分支最新代码
git pull

// 切换到B分支
git checkout B

// 获取B分支最新代码
git pull

// 合并指定文件或者文件夹到分支
git checkout A README.md 

如果这里想要合并文件夹的话,比如src/views文件夹,可以将READMD.md换成文件夹src/views/**

撤销commit、add

如果合并错误,那么就先撤销commit,然后撤销add。

// 撤销commit 
git reset --soft HEAD

// 撤销add
git reset HEAD

// 撤销指定的文件
git reset HEAD src/views/README.md

查看分支合并情况

git log --graph --pretty=oneline --abbrev-commit
# --graph 伪图形化查看分支合并图
# --pretty=oneline 单行省略显示(将不显示提交者和提交时间信息)
# --abbrev-commit  缩略显示版本ID

贮藏

贮藏当前操作

git stash

查看贮藏列表

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log

应用贮藏

# 应用的同时删除缓存堆栈中的第一个stash
git stash pop

# 将缓存堆栈中的stash多次应用到工作目录中,但并不删除stash拷贝。
git stash apply
# 应用指定的stash
git stash apply stash@{0}
# 删除则通过
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)
# 或者删除所有缓存的stash
git stash clear

遴选

复制一个特定的提交到当前分支

git cherry-pick <commit id>

删除 untracked files(未监控)文件

// 删除 untracked files
git clean -f

// 连 untracked 的目录也一起删掉
git clean -fd

// 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
git clean -xfd

// 在用上述 git clean 前,强烈建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd

查看子模块版本

// 两种方式
git submodule
git submodule status

删除tag(标签)

删除本地标签

$ git tag -d v1.1.0
$ git tag --delete v1.1.0

# 列出所有以 `v2.31.5` 开头的本地标签(tags),然后通过 `xargs` 逐个删除这些本地的标签。
$ git tag -l 'v2.31.5*' | xargs git tag -d

检查本地标签是否被删除:

# 这个命令将会列出所有匹配本地标签。如果输出为空,则表示没有这样的本地标签了。
$ git tag -l 'v2.31.5*'

删除远程标签

$ git push --delete origin v1.1.0

# 如果你想删除所有以 `v2.31.5` 开头的远端标签,你可以使用以下命令:
# 这个命令将会列出所有匹配的本地标签,然后对每个标签执行一个 `git push origin --delete <tagname>` 的操作来删除远端的标签。
$ git tag -l 'v2.31.5*' | xargs -n 1 -I {} git push origin --delete {}

检查远端标签是否被删除:

# 这个命令将会显示所有匹配远端标签。在这里,`origin` 是远端的名字,如果你使用的是其他名称,需要相应地替换它。如果输出为空,说明远端标签也已经被删除了。
$ git ls-remote --tags origin 'v2.31.5*'

查看所有的提交,包括那些还没有合并到当前分支的提交

$ git log --graph --decorate --oneline --all