本文记录我学习和使用 git 的笔记,记录学习和使用 git 时遇到的问题和技巧。(持续更新…)

分支操作

  • 拉取远程指定分支 $ git clone -b branch_name git@github.com:xxx

  • 查看所有分支 $ git branch -a

  • 切换到指定的远程分支 $ git switch -c branch_name origin/branch-name

  • 删除远程指定分支 $ git push origin -d [branch_name]

git stash 储藏当前工作区

  • git stash命令用于将当前未提交(工作区和暂存区)的改动储藏起来,等处理完其它工作之后再恢复。
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
# 查看当前工作区
$ git status
On branch dev
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: app.js

no changes added to commit (use "git add" and/or "git commit -a")
# 储藏当前工作区
$ git stash
Saved working directory and index state WIP on dev: e146062 edit app.js
# 查看 stash 记录
$ git stash list
stash@{0}: WIP on dev: e146062 edit app.js
# 此时可以切换分支或进行其他工作
# 恢复最近一次的 stash, 并删除本次 stash 记录
$ git stash pop
On branch dev
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: app.js

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (771daab2d3e6c5adcff439e7856e89891b35b5f3)
  • 可以多次stash,恢复的时候使用git stash list查看 stash 记录,然后恢复指定的 stash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看储藏记录
$ git stash list
stash@{0}: WIP on dev: e146062 edit app.js
stash@{1}: WIP on dev: e146062 edit app.js
# 恢复 stash@{0}
$ git stash apply stash@{0}
On branch dev
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")
# 删除 stash@{0} 这条记录
$ git stash drop stash@{0}
Dropped stash@{0} (72bddc948a1fa80b9e84a072a6ab1f412c9f3b22)
# 以上两条命令等同于 git stash pop stash@{0}
  • 可以看到以上两个储藏记录的标识信息完全一样,容易分不清每次储藏的什么内容,在使用 stash 时可以用-m|--message标识本次 stash
1
$ git stash -m "stash message"
  • git stash默认不会储藏未跟踪(untracked)的文件,可以使用-u|--include-untracked储藏包括未跟踪的文件;或者使用-a|--all储藏包括未跟踪和忽略(.gitignore中指定)的文件
1
2
3
4
5
6
7
# git stash -um "add index.js" 包括未跟踪的文件
$ git stash --include-untracked --message "add index.js"
Saved working directory and index state On dev: add index.js
# 或者
# git stash -am "add index.js" 包括未跟踪和忽略的文件
$ git stash --all --message "add index.js"
Saved working directory and index state On dev: add index.js

git rebase 变基

👉 git rebase 用法详解与工作原理

合并多个commit

  • git rebase 命令用来在另一个分支基础之上重新应用提交,用于把一个分支的修改合并到当前分支。
  • 也可以利用这个命令来合并多个某个分支的多条 commit。

例如我的 git 历史提交中有这样的信息:

1
2
3
4
5
6
7
8
9
$ git log --oneline
a9753cc (HEAD -> master) edit app.js
eb6604c edit app.js
8239289 edit app.js
2bb4ff1 create app.js
c81bd04 edit README.md
0a38a95 edit README.md
e87226d edit README.md
c30085a create README.md

可以看到有很多 commit 的信息都是重复的。

这时就可以使用以下命令重新编辑以往7次的 commit 历史:

1
2
3
4
# 从HEAD版本开始往过去数7个版本(包含HEAD版本)
$ git rebase -i HEAD~7
# 指定版本号前的所有版本(不包含此版本)
$ git rebase -i c30085a

-i(--interactive):弹出交互式的界面进行编辑合并

输入命令,按下回车后会弹出 vim,让你编辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ git rebase -i HEAD~7
pick e87226d edit README.md
pick 0a38a95 edit README.md
pick c81bd04 edit README.md
pick 2bb4ff1 create app.js
pick 8239289 edit app.js
pick eb6604c edit app.js
pick a9753cc edit app.js

# Rebase c30085a..a9753cc onto c30085a (7 commands)
#
# Commands:
# p, pick <commit> = 正常选中
# r, reword <commit> = 选中,并且修改提交信息
# e, edit <commit> = 选中,rebase时会暂停,允许你修改这个commit
# s, squash <commit> = 选中,会将当前commit与上一个commit合并,保留所有commit信息
# f, fixup [-C | -c] <commit> = 与squash相同,但不会保存当前commit的提交信息,
# unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor
# x, exec <command> = 执行其他shell命令
# b, break = 停在这里,可以使用'git rebase --continue'继续rebase
# d, drop <commit> = 删除这个commit,这次提交的所有更改也会丢弃
# l, label <label> = label current HEAD with a name

编辑如下,然后 :wq 保存退出

1
2
3
4
5
6
7
8
9
10
11
# 把 c81bd04 和 0a38a95 合并到 e87226d,不保留 0a38a95 和 c81bd04 的 commit message
pick e87226d edit README.md
f 0a38a95 edit README.md
f c81bd04 edit README.md
pick 2bb4ff1 create app.js
# 把 a9753cc 和 eb6604c 合并到 8239289,保留所有 commit message
pick 8239289 edit app.js
s eb6604c edit app.js
s a9753cc edit app.js

# ...

然后又会弹出一个 vim 让编辑 commit message,编辑如下,然后 :wq 保存退出

1
2
3
4
5
6
7
8
9
# This is a combination of 3 commits.
# This is the 1st commit message:
edit app.js
# This is the commit message #2:
add main fn
# This is the commit message #3:
update server port

# ...

这时再查看合并后的 log,一下就简洁了好多

1
2
3
4
5
$ git log --oneline
714f406 (HEAD -> master) edit app.js
e17c8ac create app.js
949598c edit README.md
c30085a create README.md

解决冲突

  • git rebase 时可能会发生冲突,这时就需要先去解决冲突。
  • 解决完冲突后再 git add .,然后继续 rebase git rebase --continue
  • 如果被合并的 commit,合并前已经推送到了远程库,可能无法正常推送到远程库,可以使用 --force
1
2
# 如果是多人协作开发不建议使用
$ git push --force origin master

修改 commit 的信息

修改上一次提交的 commit 信息:

1
$ git commit --amend --message="commit message"

修改历史提交的 commit 信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ git log --oneline
a2cf832 (HEAD -> master) edit app.js
8e5130d insert one line from app.js
4968028 delete one line from app.js
e17c8ac create app.js

# 修改 8e5130d 和 4968028 的 commit message
$ git rebase -i HEAD~3 # 或者 git rebase -i e17c8ac

# 把要修改的 commit 前的 pick 改为 r 或 reword
r 4968028 delete one line from app.js
r 8e5130d insert one line from app.js
pick a2cf832 edit app.js

# :wq 保存并退出,然后依次修改 commit message,:wq 保存并退出
$ git log --oneline
e146062 (HEAD -> master) edit app.js
c1fe1bf 从 app.js 中新增一行
91ea1e8 从 app.js 中删除一行
e17c8ac create app.js
# 就修改成功了

git tag 标签

git tag <tagName> 用于在版本库中创建一个标签,它其实就是指向某个 commmit 的指针,打标签时的版本快照,常被用来标记发布节点(v1.0, v2.0 …)。

查看标签

  • 查看已有的标签,输入 git tag(可带上可选的 -l--list 选项):

    1
    2
    3
    $ git tag
    v1.0
    v2.0
  • 也可以按照特定的模式查找标签(此时必须提供 -l--list 选项)

    1
    2
    3
    4
    $ git tag -l "v1.8.5*"
    v1.8.5
    v1.8.5-rc0
    v1.8.5.1

创建标签

  • 使用 git tag <tagName> 可以在当前分支最新 commit 上创建一个标签:

    1
    $ git tag v1.0
  • 创建带有说明的标签,使用 -a 指定标签名,-m 指定说明信息:

    1
    $ git tag -a v1.2 -m "version 1.2"
  • 如果想在过去的某个 commit 上打标签,只需要提供 commit id 就行了:

    1
    2
    3
    4
    5
    6
    $ git log --oneline
    7eb4a6a started write support
    9666e66 updated rakefile
    4f74ac2 commit the todo
    5317aa2 updated readme
    $ git tag -a v1.1 4f74ac2

查看标签信息

使用 git show <tagName> 可以查看某个标签的信息:

1
2
3
4
5
6
7
8
9
10
11
12
$ git show v1.2
tag v1.2
Tagger: zxyong <1203123788@qq.com>
Date: Fri Mar 10 16:48:05 2023 +0800

version 1.2

commit 1133b14a0520ed88398fbece124063ea2c99245f (tag: v1.2)
Author: zxyong <1203123788@qq.com>
Date: Fri Mar 10 16:38:36 2023 +0800

changed the version number

推送标签

默认情况下,git push 命令并不会推送标签到远程仓库上。在创建完标签后你必须显式地推送标签到远程库。

  • 使用 git push <remote> <tagname>

    1
    2
    3
    4
    5
    6
    7
    8
    $ git push origin v1.5
    Counting objects: 14, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (12/12), done.
    Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done.
    Total 14 (delta 3), reused 0 (delta 0)
    To git@github.com:schacon/simplegit.git
    * [new tag] v1.5 -> v1.5
  • 如果想要一次性推送本地的所有未推送的标签,使用 --tags 选项:

    1
    2
    3
    4
    5
    6
    7
    $ git push origin --tags
    Counting objects: 1, done.
    Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done.
    Total 1 (delta 0), reused 0 (delta 0)
    To git@github.com:schacon/simplegit.git
    * [new tag] v1.4 -> v1.4
    * [new tag] v1.4-lw -> v1.4-lw

删除标签

  • 删除本地标签,使用 git tag -d <tagname>

    1
    2
    $ git tag -d v1.4-lw
    Deleted tag 'v1.4-lw' (was e7d5add)
  • 删除远程库标签:

    1
    2
    $ git push origin --delete v1.4
    # 或者 git push origin :refs/tags/v1.4