Git Workflow and Basic-Commandlhmouse 🚩🌱

Git 工作指南

作为一个非常有历(wei)史(dao)的 版本控制系统 ,Git 理所当然地存在很多历史遗留问题。然而我们不想解决历史遗留问题——它们实在是太多了。 所以,你应该学会叫 Git 这个 git(饭桶)闭嘴;至少,不要叫那些历史遗留的 s@#t 来污染你的大脑。

如果你不能理解本文的内容,照着做就是了;如果你连照着做也做不到,就不要用 Git。

获得一个文本编辑器

Git 有时候需要你使用 文本编辑器 ,例如使用 git commit 的时候它会提示你输入提交注释,你使用 git rebase -i 的时候它会将即将执行的命令的清单显示给你并且你可能会对其进行修改。那个叫 vim不配 被称为文本编辑器。我们推荐使用 GNU nano。如果你在用 Windows,可以试试 这个

修改全局设置

在你能好好地使用 Git 管理你的代码之前,把这几个设置改一下:

$ git config --global color.status auto           # 使 git status -s 命令的输出带有颜色。
$ git config --global color.diff auto             # 使 git diff 命令的输出带有颜色。
$ git config --global color.branch auto           # 使 git branch -a 命令的输出带有颜色。
$ git config --global color.interactive true      # 使 git add -i 命令的输出带有颜色。
$ git config --global core.editor /usr/bin/nano   # 请不要理会那个竟然敢自称编辑器的叫 vi 的智障。
$ git config --global core.pager "less -x1,5"     # 设置好 diff 的对齐。 [1]
$ git config --global core.autocrlf input         # 别把 CR LF 提交到服务器上。
$ git config --global push.default simple         # 仅 push 当前分支。
$ git config --global pull.ff only                # 禁用非 --ff-only 的 pull 操作。
$ git config --global merge.ff only               # 禁用非 --ff-only 的 merge 操作。

[1] http://lists.gnu.org/archive/html/nano-devel/2017-12/msg00117.html

开发者手册

  1. 《Git 工作流程》
    http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
    仅参考其中的 Gitlab flow。 master 分支(debug/unstable)仅作开发,产品分支(release/stable)仅以 cherry-pick 更新。

  2. 《常用 Git 命令清单》
    http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html

  3. 《Git 使用规范流程》
    http://www.ruanyifeng.com/blog/2015/08/git-use-process.html

禁止事项

  1. 禁止直接在 master 分支上开发。
  2. 禁止直接向 master 和 release/stable 分支 push。
  3. 禁止 merge, --ff-only 除外。
  4. 禁止 pull, --ff-only--rebase 除外。
  5. 文本文件禁止使用宽字符编码,非手动编辑的文件除外。

开发流程

一般开发流程

  1. 获取 最新代码:
    $ git checkout master
    $ git fetch
    $ git reset --hard origin/master
    
  2. 创建 用于开发的 本地分支 (此处假定用于开发的分支名为 working):
    $ git checkout -b working
    
  3. 在 working 分支上 进行修改
  4. 提交修改 到 working 分支:
    $ git add 第一个文件
    $ git add 第二个文件
    $ git commit
    
  5. 将本地分支 working 推送 到服务器上,创建一个新的 远端分支 origin/working:
    $ git push origin working
    
  6. 在 GitLab 上查看推送的分支, 创建 到 master 分支的 Merge Request
  7. 查看新建的 Merge Request,如果没有冲突,点击 合并
  8. 再次 获取 最新的代码:
    $ git checkout master
    $ git pull --ff-only
    
  9. 删除本地的 开发 分支
    $ git branch -d working
    
  10. 删除服务器上的 开发 分支
    $ git push origin :working
    

如何解决冲突:

  1. 获取 最新代码:
    $ git fetch
    
  2. 如果本地有没提交的修改, 提交 之,否则无法进行变基操作。
  3. 执行 变基 操作,基于新的远端分支 origin/master:
    $ git rebase origin/master
    
  4. 如果提示 CONFLICT, 查看 当前变基 状态
    $ git status -s
    
    M  暂存区  变基并且没有冲突的文件
     M 工作区  修改但是没有提交的文件
    ?? 非追踪  没有加入版本控制的文件
    UU 冲突   变基出现冲突的文件
    
  5. 解决冲突:
    1. 查找 <<<<<<< (7 个)标记并进行编辑,保存为要保留的内容,然后标记为 已解决
      $ git add 变基出现冲突的文件
      
    2. 丢弃本地的修改 ,使用服务器的文件(对于变基操作 --ours基点 ):
      $ git checkout --ours 变基出现冲突的文件
      $ git add 变基出现冲突的文件
      
    3. 丢弃服务器的修改 ,使用本地的文件(对于变基操作 --theirs本地 ):
      $ git checkout --theirs 变基出现冲突的文件
      $ git add 变基出现冲突的文件
      
  6. 重复 3~5 直到解决所有冲突,然后 继续变基操作
    $ git rebase --continue
    
  7. 重复 3~6 直到变基完成。
  8. 用本地的 working 分支 覆盖服务器上的 working 分支
    $ git push origin +working
    
  9. 重新尝试合并 出现冲突的 Merge Request。

如果变基到一半不想变基了怎么办

  1. 取消变基操作 ,本地已提交的修改不会丢失:
    $ git rebase --abort
    

切记

  1. 切换分支之前把本地需要提交的修改全部提交。
  2. 如果涉及到合并或重置代码,创建新的分支做。原有分支不会受到影响。  
  3. 没事不要 merge。
  4. 没事不要 merge。
  5. 没事不要 merge。

对于已 fork 的仓库怎么合并上游修改、发布补丁

(以下假定下游远端为 origin,上游远端为 upstream,双方的主分支都是 master。)

  1. 获取上游仓库:
    $ git fetch upstream
    
  2. 以当前分支为原本,创建一个新的分支用于变基:
    $ git checkout -b rebasing
    
  3. 对本地修改进行变基:
    $ git rebase upstream/master
    
  4. 如果遇到冲突,解决冲突。
  5. 变基结束后,测试新的分支。
  6. 如果测试没有问题,用新的分支 替换 原来的分支:
    $ git checkout -B master
    
  7. 用本地的 master 分支 覆盖服务器上的 master 分支
    $ git push origin +master
    
  8. 将本地相对于上游新增的补丁导出成 .patch 文件(可以使用电子邮件发送给上游):
    $ git format-patch upstream/master
    

合并下游补丁

  1. 应用补丁文件:

    $ git am 第一个补丁文件 第二个补丁文件
    
  2. 推送到远端分支

    $ git push
    

从其他分支挑拣单个提交

  1. 挑拣单个提交:

    $ git cherry-pick 单个提交散列标识
    
  2. 推送到远端分支

    $ git push
    



Git 入门命令参考

为了降低入门难度,这部分参考的内容是按照 常用操作 而不是按照命令排序的。由于 Git 命令被 极其糟糕 地重载, 去读他喵的手册(RTFM) 虽然是正统且全面的指南,但是被认为不能帮助新手很快地投入日常使用。

格式说明

本文中所有的 术语(term) 皆以 斜体字 标出。术语 的含义是 明确的严格的不可替代的。在 术语 首次出现时,它会被标明对应的英文原文。

命令参考

入门命令参考

仓库(repository) 克隆(clone) 到本地文件系统。

克隆到和仓库同名的文件夹:

git clone <仓库地址>

克隆到指定的文件夹:

git clone <仓库地址> [--] <目标文件夹>

添加 工作区(working tree) 已修改的文件到 暂存区(index) 等待提交(开始跟踪)

添加文件或一个文件夹下的所有未被忽略的文件:

git add [--] <待添加的文件或文件夹>...

强制添加已被忽略的文件或文件夹:

git add -f [--] <待添加的文件或文件夹>...

撤销 git add 操作

撤销文件或文件夹下的所有文件(方法一):

git reset [--] <待撤销的文件或文件夹>...

撤销文件或文件夹下的所有文件(方法二):

git rm --cached [--] <待撤销的文件>...
git rm --cached -r [--] <待撤销的文件夹>...

提交 暂存区 的修改

创建新的 提交(commit),打开文本编辑器书写提交日志:

git commit

创建新的 提交,直接在命令行上写入提交日志:

git commit -m <提交日志>

修改最后一次 提交,打开文本编辑器书写提交日志:

git commit --amend

修改最后一次 提交,直接在命令行上写入提交日志:

git commit --amend -m <提交日志>

绕过 暂存区 提交指定的文件

打开文本编辑器书写提交日志(如果待提交的文件或文件夹已经从文件系统删除,就必须使用 -- 来标示路径):

git commit [--] <待提交的文件或文件夹>...

直接在命令行上写入提交日志(如果待提交的文件或文件夹已经从文件系统删除,就必须使用 -- 来标示路径):

git commit -m <提交日志> [--] <待提交的文件或文件夹>...

修改最后一次 提交,打开文本编辑器书写提交日志(如果待提交的文件或文件夹已经从文件系统删除,就必须使用 -- 来标示路径):

git commit --amend [--] <待提交的文件或文件夹>...

修改最后一次 提交,直接在命令行上写入提交日志(如果待提交的文件或文件夹已经从文件系统删除,就必须使用 -- 来标示路径):

git commit --amend -m <提交日志> [--] <待提交的文件或文件夹>...

移除已经提交的文件或文件夹(停止跟踪)

移除文件或文件夹并删除它,如果有未提交的修改则该操作失败:

git rm [--] <待移除的文件>...
git rm -r [--] <待移除的文件夹>...

移除文件或文件夹并删除它,如果有未提交的修改则丢弃之:

git rm -f [--] <待移除的文件>...
git rm -fr [--] <待移除的文件夹>...

撤销最后一次的 git commit 操作

撤销提交,被提交的修改进入 工作区

git reset HEAD~1

撤销提交,被提交的修改进入 暂存区

git reset --soft HEAD~1

撤销提交,丢弃已提交的修改:

git reset --hard HEAD~1

还原对文件或文件夹的修改

将文件或文件夹的修改还原至 暂存区 的状态:

git checkout [--] <待还原的文件或文件夹>...

将文件或文件夹的修改还原至最后一次提交的状态:

git checkout HEAD [--] <待还原的文件或文件夹>...

将文件或文件夹的修改还原至上一次提交的状态:

git checkout HEAD~1 [--] <待还原的文件或文件夹>...

将文件或文件夹的修改还原至其他分支的状态:

git checkout <源分支> [--] <待还原的文件或文件夹>...

丢弃所有修改

丢弃 工作区 的所有修改:

git checkout -- :/

丢弃 暂存区工作区 的所有修改(方法一):

git checkout HEAD -- :/

丢弃 暂存区工作区 的所有修改(方法二):

git checkout -f

丢弃 暂存区工作区 的所有修改(方法三):

git reset --hard

解决 冲突(conflict)

使用 工作区 的文件来解决冲突:

git add <冲突的文件>...

删除指定的文件来解决冲突:

git rm --force <冲突的文件>...

从其他分支提取文件或文件夹

从其他分支提取文件或文件夹进入 暂存区(如果文件参数是当前文件系统中不存在的文件,就必须使用 -- 来标示路径):

git checkout <源分支> [--] <待提取的文件或文件夹>

提取发生 冲突 的文件的对应当前分支(对于 git merge)或目标分支(对于 git rebase)上的文件或文件夹进入 工作区,保留冲突状态:

git checkout --ours [--] <待提取的文件或文件夹>

提取发生 冲突 的文件的对应目标分支(对于 git merge)或当前分支(对于 git rebase)上的文件或文件夹进入 工作区,保留冲突状态:

git checkout --theirs [--] <待提取的文件或文件夹>

清理不需要的文件

删除 工作区 中当前目录下的所有未被跟踪的文件和文件夹,包括已忽略的:

git clean -fdx

删除 工作区 中当前目录下的所有未被跟踪的文件和文件夹,除了已忽略的:

git clean -fd

删除 工作区 中当前目录下的所有已忽略的文件和文件夹:

git clean -fdX

查看文件状态

查看 暂存区工作区 的所有文件状态:

git status -s
M     已向暂存区添加
D     已从暂存区删除
 M    已在工作区修改
 D    已从工作区删除
UU    冲突
??    未跟踪

查看分支

查看 本地分支(local branch)

git branch

查看 本地分支(local branch)远端分支(remote branch)

git branch -a
master                  本地分支 master
remotes/origin/master   远端分支 origin/master (对应 origin 远端上的 master 分支)
remotes/upstream/temp   远端分支 upstream/temp (对应 upstream 远端上的 temp 分支)

切换分支

切换到指定的分支:

git checkout <目标分支>

切换到上一次切换以前的分支:

git checkout -

创建分支

创建一个和当前分支一样的分支并保持当前分支不变,如果新分支已存在则该操作失败:

git branch <新分支>

创建一个和当前分支一样的分支并切换到新分支,如果新分支已存在则该操作失败:

git checkout -b <新分支>

创建一个和当前分支一样的分支并切换到新分支,如果新分支已存在则覆盖之:

git checkout -B <新分支>

删除分支

删除 本地分支,如果分支未被 合并(merge)上游分支(upstream branch) 则该操作失败:

git branch -d <旧分支>

删除 本地分支,不检查合并状态:

git branch -D <旧分支>

重命名分支

重命名 本地分支

git branch -m <旧分支> <新分支>

远端 更新分支到本地的 远端分支

更新当前分支的 上游分支远端 上的所有分支到本地的 远端分支

git fetch

更新指定 远端 上的所有分支到本地的 远端分支

git fetch <远端>

更新所有 远端 上的所有分支到本地的 远端分支

git fetch --all <远端>

从其他分支更新数据到本地的 本地分支

将其它分支 合并(merge) 到当前分支(创建合并节点,当前分支变为该节点的子分支):

git merge <目标分支>...

将另一条分支 合并 到当前分支,如果目标分支不是当前分支的子分支则该操作失败:

git merge --ff-only <目标分支>

将当前分支 变基(rebase) 到另一条分支(将两条分支不同部分切除,然后拼接在目标分支之后,结果保存为当前分支):

git rebase <目标分支>

复制另一条分支到当前分支:

git reset --hard <目标分支>

远端 更新数据到本地的 远端分支本地分支

更新当前分支的 上游分支远端 上的所有分支到本地的 远端分支,再使用 git merge 更新 本地分支

git pull

更新当前分支的 上游分支远端 上的所有分支到本地的 远端分支,再使用 git merge --ff-only 更新 本地分支

git pull --ff-only

更新当前分支的 上游分支远端 上的所有分支到本地的 远端分支,再使用 git rebase 更新 本地分支

git pull --rebase

更新 远端 上的分支

更新当前分支到其 上游分支

git push

更新指定的 本地分支 到指定 远端 上的 上游分支,如果 本地分支 没有 上游分支 则该操作失败:

git push <远端> <本地分支>

更新指定的 本地分支 到指定 远端 上的 上游分支,如果 本地分支 没有 上游分支 则使用同名的分支作为 上游分支 并设置之:

git push -u <远端> <本地分支>

更新当前分支到指定 远端 上的指定分支:

git push -u <远端> HEAD:<分支>

更新指定的 本地分支 到指定 远端 上的指定分支:

git push -u <远端> <本地分支>:<分支>

删除指定 远端 上的指定分支:

git push -u <远端> :<分支>