-Git 使用技巧 总结 MD

博文地址

我的GitHub 我的博客 我的微信 我的邮箱
baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目录

Bash下的快捷操作

常用命令

  • cd:change directory 改变目录,bash中不带参数时默认进入【/c/Users/当前用户】目录,cmd中不带参数时等价于pwd
  • cd -:回到前一个目录,cmd中没有此命令
  • cd ..:回到父目录
  • cd aaa:进入指定目录,可以是相对目录或绝对目录
  • pwd:打印工作目录路径
  • lsll:列出当前目录中的所有文件,cmd中没有ll命令
  • touch aaa.txt:在当前目录下新建一个文件
  • mkdir aaa:在当前目录下新建一个目录
  • rm aaa.txt:删除指定的文件
  • rm -r aaa:删除指定的目录,参数r为recusive是递归的意思
  • mv aaa.txt bbb:将文件移到指定目录下
  • q:结束一个命令
  • explorer aa:打开指定的目录或文件

常用操作

  • 复制内容:选中后就已经复制了(勾选了"选择时复制"),或 Ctrl + Insert
  • 粘贴内容:点击鼠标中键,或 Shift + Insert
  • 搜索内容:Alt + F3,按EnterShift + Enter选中下/上一个匹配内容
  • 缩放文字:Ctrl + 【+/-】
  • 窗口清屏:Alt + F8(重置),cmd 下的命令是 cls【重要】
  • 窗口全屏:Alt + F11,再按一次还原

移动光标

  • 一个单词一个单词向左/右移动光标:Alt + B/F【重要】
  • 移动光标到整条命令的起始/结束位置:Ctrl + A/E【重要】
  • 一个字符一个字符向左/右移动光标:Ctrl + B/F

删除输入内容

  • 删除光标左/右侧的所有内容:Ctrl + U/K【重要】
  • 删除光标侧的单词:Ctrl + W【重要】
  • 删除光标侧的单词:Alt + D
  • 删除光标左/右侧的字符:Ctrl + H/D(或backspace键、delete键)

Tab键的作用

  • 输入内容(文件名或命令)的前几个字母时,按tab,如果有内容可以匹配,它就会显示出完整的内容
  • 如果有多个内容匹配到,它会显示最先找到的一个,再按一次tab,它就会匹配的下一个
  • 我们可以不停地按Tab键在匹配的内容中来回切换,直到找到我们文件名为止

Git默认Vim编辑器基本使用

Git默认的编辑器是Vim,在很多情境下会遇到它,例如commit提交,如果提供-m指令,直接在后面写上此次提交的说明信息;如果不提供-m指令,默认将会弹出Vim编辑器,如下:

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.

此编辑器和平时所用编辑器差距很大,Vim编辑器命令很多,下面介绍一下简单操作,以完成基本的Git操作。

Vim主要有如下两种模式:

  • Normal 模式:命令模式,按 ESCCtrl + [ 进入,此模式下无法输入内容,但是可以复制、黏贴、保存、退出
    • :w表示保存
    • :q表示离开,如果未保存会有提示
    • :q!表示不保存离开
    • :wq表示保存并离开(用的最多)
  • Insert 模式:编辑模式,点击i、a 或 o 键可以进入

Git 使用场景

合并多个commit:rebase -i【s】

基本步骤

  • 执行git rebase -i HEAD~影响的最少commit数git rebase -i 前一个commitId
  • 需要合并的那条commit前面的pick改为s,保存后退出
  • 修改commit日志,保存后退出

详细操作

在使用 Git 作为版本控制的时候,我们可能会由于各种各样的原因提交了许多临时的 commit,而这些 commit 拼接起来才是完整的任务。那么我们为了避免太多的 commit 而造成版本控制的混乱,通常我们推荐将这些 commit 合并成一个。

首先你要知道自己想合并的是哪几个提交,可以使用 git log 命令来查看提交历史,假如最近 4 条历史如下:

commit 8b5cbda494a68582164048159e605e731f357444 (HEAD -> master)
commit a472755058e78a33595390f87d03d855fc25da84
commit 6ab13045d47157842961fae70baa7ef25e1856b1
commit 00b8fe51079ac0ba1d5a87e9c0b51c9e851d233f (origin/master, origin/HEAD)

历史记录是按照时间排序的,时间近的排在前面。

如果想要合并第 2 和第 3 条记录,有两个方法,这两个方法效果是一致的:

  • 从HEAD版本开始往过去数 3 个版本
git rebase -i HEAD~3
  • 指名要合并的版本之前的版本号
git rebase -i 00b8fe51079ac0ba1d5a87e9c0b51c9e851d233f

请注意 00b8fe51079ac0ba1d5a87e9c0b51c9e851d233f 这个版本是不参与合并的,可以把它当做一个坐标

执行了 rebase 命令之后,会弹出一个窗口,内容如下:

pick 6ab1304 11111111
pick a472755 222222222
pick 8b5cbda 3333333 # Rebase 00b8fe5..8b5cbda onto 00b8fe5 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword(改写) = use commit, but edit the commit message
# e, edit = use commit, but stop for amending(修正)
# s, squash(塞入) = use commit, but meld into(融入) previous commit
# f, fixup(修正) = like "squash", but discard(丢弃) this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# Note that empty commits are commented out(被注释掉的)

可以看到,里面有详细的命令提示。我们将需要合并的那条commit前面的pick改为s,之后保存并关闭文本编辑窗口即可。改完之后文本内容如下:

pick 6ab1304 11111111
s a472755 222222222
pick 8b5cbda 3333333

然后保存退出。如果没有冲突,或者冲突已经解决,则会出现如下的编辑窗口:

# This is a combination of 2 commits.
# This is the 1st commit message: 11111111 # This is the commit message #2: 222222222 # Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit(空消息会终止提交).
#
# Date: Sun Jul 7 21:14:02 2019 +0800
#
# interactive(互动) rebase in progress; onto 00b8fe5
# Last commands done (2 commands done):
# pick 6ab1304 11111111
# squash a472755 222222222
# Next command to do (1 remaining command):
# pick 8b5cbda 3333333
# You are currently rebasing branch 'master' on '00b8fe5'.
#
# Changes to be committed:
# modified: build.gradle

如果不做任何修改的话,默认是保留两次 commit 的日志,中间有一个空白行,你可以随意修改合并后的日志。

保存并退出后再次输入 git log 查看 commit 历史信息,你会发现这两个 commit 已经合并了。

commit 9adb987d31f11f8d38f8d817096d2d21a7390a1d (HEAD -> master)
commit 052ea3ea0b30c375e79ad9e891c5e5202768b11b
commit 00b8fe51079ac0ba1d5a87e9c0b51c9e851d233f (origin/master, origin/HEAD)

虽然我们只是合并了第 2 条和第 3 条的commit,但是第 1 条的 commitId 也变了。

如果有冲突,需要修改。修改的时候要注意,保留最新的历史,不然我们的修改就丢弃了。

修改以后要记得敲下面的命令:

git add .
git rebase --continue

如果你想放弃这次压缩的话,执行以下命令:

git rebase --abort

删除多个commit:rebase -i【d】

和上面的操作基本一致。

例如我的提交历史如下:

commit 831be8ec644c4943374adca03880732c7ec9d6bd (HEAD -> master)
commit 6a8ecb529a4ec6b6e0fc83e0789043b7785e73fe
commit 3b228d8af9e52806c106ef3f0c27e622b5407faf
commit 9adb987d31f11f8d38f8d817096d2d21a7390a1d (origin/master, origin/HEAD)

我想删除第2条和第3条提交,基本步骤:

  • 执行git rebase -i HEAD~影响的最少commit数git rebase -i 前一个commitId
  • 需要删除的那几条commit前面的pick改为d,保存后退出
  • 修改commit日志,保存后退出

注意,这个操作感觉出现冲突的概率比较大,例如我上面三次commit都是在同一个文件的第一行插入一行新的文字:

//添加3
//添加2
//添加1

执行rebase后就会提示冲突了:

git rebase -i 9adb987d31f11f8d38f8d817096d2d21a7390a1d
Auto-merging build.gradle
CONFLICT (content): Merge conflict in build.gradle
error: could not apply 831be8e... 333 Resolve all conflicts manually手动, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort". Could not apply 831be8e... 333

解决冲突后按以下操作:

git add .
git rebase --continue

可以发现,相应 commit 都还原了,包括修改的文件以及提交的commit记录。

修改多个commit:rebase -i【r】

和上面的操作基本一致。

例如我的提交历史如下:

commit 442... (HEAD -> master)
commit 5a1...
commit 305...
commit f30... (origin/master, origin/HEAD)

我想修改第2条和第3条提交,基本步骤:

  • 执行git rebase -i HEAD~影响的最少commit数git rebase -i 前一个commitId
  • 需要修改的那几条commit前面的pick改为r,保存后退出
  • 每个改为reword的提交都会提示你一个编辑窗口让你修改commit日志,修改保存后退出

注意,修改后会生成新的commitId

挑选多个commit:cherry-pick

cherry-pick 的翻译是择优挑选,使用git cherry-pick命令,可以选择将现有的一个或者多个提交的修改引入当前内容。

cherry-pick 官方文档

git cherry-pick <commitid> <commitid>

挑选多个提交合并,提交之间用空格相隔。

git cherry-pick <start-commit>..<end-commit>

挑选一个范围内的多个提交合并,不包含start-commit(最早的提交),包含end-commit(最近的提交)。另外要注意两个 commit 之间要求有连续关系的,并且前者要在后者之前,顺序不能颠倒。

git cherry-pick <start-commit>^..<end-commit>

这个和上面一样,区别就是加了一个^符号,就变成闭区间了,包含 start-commit。

git cherry-pick <branch name>

挑选 branch 最顶端的提交。

git cherry-pick --continue  //继续下个操作
git cherry-pick --quit //退出
git cherry-pick --abort //停止本次操作

当 cherry-pick 多个 commit 时,假设遇到冲突:

  • --continue继续进行下个
  • --quit结束 cherry-pick 操作但是不会影响冲突之前多个提交中已经成功的
  • --abort直接打回原形,回到 cherry-pick 前的状态,包括多个提交中已经成功的

使用 git branch -d 删除分支的条件

参考:git branch -d 和 git branch -D 的区别

结论:在使用-d删除分支时,该分支必须完全和它的上游分支merge完成,如果没有上游分支,必须要和HEAD完全merge。

案例一

  • 1、现有一个本地分支 v0.1,我做一些修改后 commit (没有 commit或stash 是不能切换到其他分支的)
  • 2、然后切到其他分支(因为不能删除当前分支)后删除 v0.1 试试 ,可以发现删除失败
git branch -d v0.1
error: The branch 'v0.1' is not fully merged.
If you are sure you want to delete it, run 'git branch -D v0.1'.

3、然后切到 v0.1 上,将修改推送到远端:git push origin v0.1

4、然后再切到其他分支后删除 v0.1 试试,可以发现删除成功,但是有警告

git branch -d v0.1
warning: deleting branch 'v0.1' that has been merged to 'refs/remotes/origin/v0.1', but not yet merged to HEAD.
Deleted branch v0.1 (was 4a79623).

上面的步骤进行一些优化:

4、假如我们后面删除前是切到了 master 分支,所以当前HEAD为 master 分支("所以"这个词用的对吗?)

5、将 v0.1 上的修改 merge 过来git merge v0.1,然后再删除 v0.1 试试,可以发现删除成功且没有警告

案例二

  • 1、首先我基于当前分支 v0.1 创建了一个分支 v0.1_copy:git branch v0.1_copy(此分支并没有上游分支)
  • 2、然后切到 v0.1_copy 上,并进行一些修改,然后 commit
  • 3、然后切到 v0.1 后删除 v0.1_copy 试试,可以发现删除失败
git branch -d v0.1_copy
error: The branch 'v0.1_copy' is not fully merged.
If you are sure you want to delete it, run 'git branch -D v0.1_copy'.

5、然后切到 v0.1 上,并将 v0.1_copy 上的修改 merge 过来git merge v0.1_copy

6、然后再删除 v0.1_copy 试试,可以发现删除成功且没有警告

git branch -d v0.1_copy
Deleted branch v0.1_copy (was 03cb1c6).

总结

简单来说就是这么一个设计思想:

  • 如果一个分支有和远端分支关联(有上游分支),那么在删除此分支前需要将此分支上的commit push到远端,不然在分支被删除时在其上所做的 commit 也一并被删除了,设计者认为这样的操作风险极大
  • 如果一个分支没有和远端分支关联(没有上游分支),由于在此分支上的 commit 不能 push 到远端,那么为了防止 commit 也一并被删除了,设计者要求必须要和HEAD完全merge才能删除

总之一句话,删除分支不能导致有任何已做的 commit 无法追踪,如果你想这么做,必须使用 -D 达到你的目的

解决 push 时提示 Everything up-to-date 问题

参考 * 上的答案

问题复现

1、现在有一个本地分支 v0.1,且其和远端分支 v0.1 关联,此时你在本地 v0.1 做的任何修改都可以通过git push origin v0.1推到远端,这很正常

2、然后你通过git branch v0.1_backup创建了一个备份分支,然后 checkout 到此分支后做了一些修改,当你 commit 后 push 到远端的 v0.1 时问题就出现了

git push origin v0.1
Everything up-to-date

其实这里的 "Everything up-to-date(最新)", 的含义是:

"all the branches you've told me how to push are up to date".

也就是说远端的v0.1是最新的

并且此时其实并没有push到远端的

git status
On branch v0.1_backup
Your branch is ahead of 'origin/v0.1' by 1 commit.

原因是很简单的,分支 v0.1_backup 并没有和远端分支关联

$ git branch -vv
master de18ed6 [origin/master] 修改1
v0.1 03cb1c6 [origin/v0.1] 修改2
* v0.1_backup 88eb433 修改3

3、现在你通过git branch -u origin/v0.1 v0.1_backup可以将其和远端的 v0.1 关联

$ git branch -vv
master de18ed6 [origin/master] 修改1
v0.1 03cb1c6 [origin/v0.1] 修改2
* v0.1_backup 88eb433 [origin/v0.1: ahead 1] 修改3

4、然后再 push 到远端的 v0.1 会发现,问题依旧

解决方式

解决方式有三个:

1、改名,将本地分支名 v0.1_backup 改为和远程分支名一样的 v0.1 就可以了。神马,同名的分支已存在?那你为什么不在已存在的那个分支上操作呢?

2、merge,比如上面说的和远程分支同名的本地分支 v0.1 已存在时,可以先将 v0.1_backup 的修改 merge 到 v0.1 上,然后再在 v0.1 上 push 就可以了。虽然麻烦,但是这是标准操作(个人感觉)。

3、使用下面的命令也可以

git push origin v0.1_backup:v0.1

Author 与 Committer 有什么区别

参考

  • Author 是实际的修改者(patch 的作者),Committer 是提交者(把 patch 应用到 repository 里的人)
  • 很多项目限制只有少数人可以提交 patch,但大家(patch 的作者)都可以把 patch 发送给这些人
  • committer 只能通过 commit 得到,通过 git commit --reset-author 或者 --author="Name" 可以修改 Author(一般不会修改)

2019-7-14

上一篇:对clear float 的理解


下一篇:优化DP的奇淫技巧