跳转至

Git Tools

This part is mainly from ProGit Ch7

Tip

这部分随缘更新,用到的时候再学,学完顺便总结一下

最近在 skypilot 进行高强度开发,因此遇见很多git中曾经不重视的问题,借此机会学习并进行汇总

Stashing and Cleaning

Examples

基础环境

Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# `more` branch is created by `main` git ls-tree --name-only main
1.py
2.py
3.py
4.py
  ~/Desktop/test   main ?1                                                                                                       14:59:33
❯ git ls-tree --name-only more
1.py
2.py
3.py
5.py
  ~/Desktop/test   main ?1                                                                                                       14:59:36
❯ git branch                  
* main
  more

使用场景

现在我在more分支下新建两个文件:bxhu_add_not_commit.pybxhu_not_add.py.

Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 git add bxhu_add_not_commit.py
❯ git status
On branch more
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   bxhu_add_not_commit.py

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    bxhu_not_add.py

为了更好的说明stash的特性,我特地设置了两个文件,一个被追踪但未被提交,一个压根没被追踪。

现在的问题是,我突然要回到main分支,新建一个6.py;但是,上述两个文件都是我瞎写的,我甚至不想给它们commit,目前没法切换分支 ToT

Legal but not good

其实“目前没法切换分支”不完全准确,事实上也可以切换,但是这样很不安全,不符合规范

如果在 more 分支中添加了新文件而没有提交,当您切换回 main 分支时,这些文件会暂时出现在 main 分支的工作区。这种情况发生的原因是:

  • 暂存文件 bxhu_add_not_commit.py
    • 该文件被 git add 暂存了但未提交。Git 会把暂存的更改带到切换后的分支上,因为暂存区的内容在 Git 看来是待定的,属于未完成的变更。
  • 未跟踪文件 bxhu_not_add.py
    • 未跟踪文件会一直停留在工作区中,跨分支切换时也会存在。Git 默认不会自动移除未跟踪的文件,除非通过 git clean 清理。

因此我在这里,最规范的做法是:git stash,暂存目前的变更进入缓冲区

From ProGit

Now you want to switch branches, but you don’t want to commit what you’ve been working on yet, so you’ll stash the changes. To push a new stash onto your stack, run git stash or git stash push:

现在我们进行stash操作

stash状态1:

Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 git status
On branch more
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   bxhu_add_not_commit.py

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    bxhu_not_add.py

❯ git stash save "bxhu_not_add.py is not tracked now"
Saved working directory and index state On more: bxhu_not_add.py is not tracked now

stash状态2:

Bash
1
2
3
4
5
6
7
8
 git add bxhu_not_add.py

❯ git stash save "track bxhu_not_add.py"
Saved working directory and index state On more: track bxhu_not_add.py

❯ git status
On branch more
nothing to commit, working tree clean

检查当前stash list:

Bash
1
2
3
 git stash list
stash@{0}: On more: track bxhu_not_add.py
stash@{1}: On more: bxhu_not_add.py is not tracked now

注意这里,stash@{0}是最新的stash,stash@{1}是之前的stash,以此类推...

现在假设我们==回到了 main 分支进行一系列操作,操作完,又回到 more 分支==,我们想要恢复之前的stash,那么我们可以使用git stash apply命令:

先回到比较老的状态(stash@{1})试试:

Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  ~/Desktop/test   more *2                                                                                                                                                   15:55:35
❯ git stash apply stash@{1}
On branch more
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   bxhu_add_not_commit.py

  ~/Desktop/test   more *2 +1                                                                                                                                                16:00:08
❯ git status
On branch more
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   bxhu_add_not_commit.py

再到最新的状态(stash@{0}):

Bash
1
2
3
4
5
6
 git stash apply stash@{0}
On branch more
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   bxhu_add_not_commit.py
    new file:   bxhu_not_add.py

注意这里,stash apply之后,它会自动显示当前的status,不需要再单独操作一次了

Summary

个人来看,stash 是一个“时间机器” for specific branch,它可以让我们在当前分支上,暂存当前的工作区,然后切换到其他分支,完成其他分支的工作,然后再切换回来,恢复之前的工作区。

这里总结一下常见的 stash 指令:

1) 保存修改

Bash
1
2
git stash # save current status (staged + unstaged)
git stash save "specific message for this item" # Strongly Recommend!!!
Unstaged and Staged
  • 默认情况下,git stash 只会保存已跟踪的文件修改 (staged)
  • 使用 git stash -u 可以同时保存未跟踪的文件 (staged + unstaged)

2) 恢复修改

Bash
1
2
git stash pop # back to "timepoint" and delete corresponding stash item
git stash apply # back to "timepoint" but keep stash item

3) 查看stash list

Bash
1
git stash list # PS: Stack Structure,  stash@{0} is the latest stash

4) 删除list条目

Bash
1
2
git stash drop stash@{n} # delete specific item in Stash List
git stash clear          # delete all items in Stach List

My Choice:

  • git stash save "msg"
  • git stash list
  • git stash apply stash@{n}
  • git stash drop stash@{n}

Rebase and Merge

事实上这是一个历史遗留的疑惑点,正好今天有时间系统整理一下:

This video is all you need :)

Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# stage 1 in main
git init
echo "init content" >> file.txt
git add file.txt
git commit -m "init file"

# stage 2 in feature
git checkout -b feature
echo "modified by feature branch" >> file.txt
git add file.txt
git commit -m "add new content in file"

# stage 3 in main
git checkout main
echo "modified by main branch" >> file.txt
git add file.txt
git commit -m "add new functionality in main"

现在 commit timeline 长这样:

alt text

Difference

1)如果采取merge方案:

Bash
1
2
git checkout main
git merge feature

alt text

2)如果采取rebase方案:

Bash
1
2
3
4
git checkout feature
git rebase main
git checkout main
git merge feature

中间态,在git rebase main之后

alt text

最终态:after git merge feature

alt text

Exception Process

在feature分支merge到main分支的过程中,会出现一些常见的conflict错误,这里我们分类来说明解决方法:

Merge Conflict

首先确保你已经修改了有冲突的文件(file.txt),删除了冲突标记(<<<<<<, =======, >>>>>>>)并保存了最终想要的内容。

这一步:

  • 如果在vim中,就是逐个对比<<<<<<, =======, >>>>>>>区间内的异同,手动修改;
  • 如果在vscode中,可以利用“冲突合并编辑器”,直接点击“接受当前更改”或“接受传入更改”来解决冲突,非常方便 :)

1) 将修改后的文件添加到暂存区:

Bash
1
git add file.txt

2) 提交合并结果:

Bash
1
git commit -m "Merge branch 'feature': resolve conflicts in file.txt"

PS, 如果在解决冲突过程中改变主意了,想要取消这次合并,可以使用:

Bash
1
git merge --abort

3) 如果之后需要推送到 remote repo,直接推送即可:

Bash
1
git push origin <branch-name>
检查状态的cmd
  • git status, 查看当前冲突状态
  • git diff, 查看具体的冲突内容

Rebase Conflict

首先确保你已经修改了有冲突的文件(file.txt),删除了冲突标记(<<<<<<, =======, >>>>>>>)并保存了最终想要的内容。

这里跟上面merge一模一样。

1) 将修改后的文件添加到暂存区:

Bash
1
git add file.txt

2) 继续 rebase 过程:

Bash
1
git rebase --continue
abort in rebase

在 rebase 过程中可能需要多次解决冲突

放弃这次 rebase,回到之前的状态 (最初始状态):

Bash
1
git rebase --abort

小心:这是回到最初始,而不是上一步状态!

3) rebase 完成后,如需推送到远程,要使用强制推送:

Bash
1
git push -f origin <branch-name>