From 90095b1cccb45715d935298e435efdcf7bbc25f7 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 11 Apr 2018 11:27:36 +0800 Subject: [PATCH] =?UTF-8?q?:bookmark:=20Git=20=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/git/README.md | 26 +- docs/git/appendix/git-faq.md | 882 ++++++++++++++++++--------- docs/git/appendix/git-resource.md | 41 +- docs/git/basics/git-configuration.md | 220 +++++++ 4 files changed, 851 insertions(+), 318 deletions(-) create mode 100644 docs/git/basics/git-configuration.md diff --git a/docs/git/README.md b/docs/git/README.md index 7c70e1d..b4db8a2 100644 --- a/docs/git/README.md +++ b/docs/git/README.md @@ -2,19 +2,13 @@ ## 目录 -### [快速指南](git-quickstart.md) - -### 基础篇(basics) - -* [安装](basics/git-installation.md) -* 配置(configuration) - -### 进阶篇(advanced) - -* [安装](advanced/git-flow.md) - -### 附录(appendix) - -* [命令](appendix/git-command.md) -* [常见问题](appendix/git-faq.md) -* [资源](appendix/git-resource.md) +* [快速指南(quickstart)](git-quickstart.md) +* 基础篇(basics) + * [安装(installation)](basics/git-installation.md) + * [配置(configuration)](basics/git-configuration.md) +* 进阶篇(advanced) + * [git-flow 工作流](advanced/git-flow.md) +* 附录(appendix) + * [常见问题(faq)](appendix/git-faq.md) + * [命令(command)](appendix/git-command.md) + * [资源(resource)](appendix/git-resource.md) diff --git a/docs/git/appendix/git-faq.md b/docs/git/appendix/git-faq.md index c887e83..bdec721 100644 --- a/docs/git/appendix/git-faq.md +++ b/docs/git/appendix/git-faq.md @@ -1,612 +1,914 @@ - [Git 常见问题](#git-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98) - - [帮助](#%E5%B8%AE%E5%8A%A9) - - [展示帮助信息](#%E5%B1%95%E7%A4%BA%E5%B8%AE%E5%8A%A9%E4%BF%A1%E6%81%AF) - - [回到远程仓库的状态](#%E5%9B%9E%E5%88%B0%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93%E7%9A%84%E7%8A%B6%E6%80%81) - - [重设第一个commit](#%E9%87%8D%E8%AE%BE%E7%AC%AC%E4%B8%80%E4%B8%AAcommit) - - [展示工作区和暂存区的不同](#%E5%B1%95%E7%A4%BA%E5%B7%A5%E4%BD%9C%E5%8C%BA%E5%92%8C%E6%9A%82%E5%AD%98%E5%8C%BA%E7%9A%84%E4%B8%8D%E5%90%8C) - - [展示暂存区和最近版本的不同](#%E5%B1%95%E7%A4%BA%E6%9A%82%E5%AD%98%E5%8C%BA%E5%92%8C%E6%9C%80%E8%BF%91%E7%89%88%E6%9C%AC%E7%9A%84%E4%B8%8D%E5%90%8C) - - [展示暂存区、工作区和最近版本的不同](#%E5%B1%95%E7%A4%BA%E6%9A%82%E5%AD%98%E5%8C%BA%E3%80%81%E5%B7%A5%E4%BD%9C%E5%8C%BA%E5%92%8C%E6%9C%80%E8%BF%91%E7%89%88%E6%9C%AC%E7%9A%84%E4%B8%8D%E5%90%8C) - - [快速切换分支](#%E5%BF%AB%E9%80%9F%E5%88%87%E6%8D%A2%E5%88%86%E6%94%AF) - - [删除已经合并到master的分支](#%E5%88%A0%E9%99%A4%E5%B7%B2%E7%BB%8F%E5%90%88%E5%B9%B6%E5%88%B0master%E7%9A%84%E5%88%86%E6%94%AF) - - [展示本地分支关联远程仓库的情况](#%E5%B1%95%E7%A4%BA%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF%E5%85%B3%E8%81%94%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93%E7%9A%84%E6%83%85%E5%86%B5) - - [关联远程分支](#%E5%85%B3%E8%81%94%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF) - - [列出所有远程分支](#%E5%88%97%E5%87%BA%E6%89%80%E6%9C%89%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF) - - [列出本地和远程分支](#%E5%88%97%E5%87%BA%E6%9C%AC%E5%9C%B0%E5%92%8C%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF) - - [创建并切换到本地分支](#%E5%88%9B%E5%BB%BA%E5%B9%B6%E5%88%87%E6%8D%A2%E5%88%B0%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF) - - [创建并切换到远程分支](#%E5%88%9B%E5%BB%BA%E5%B9%B6%E5%88%87%E6%8D%A2%E5%88%B0%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF) - - [删除本地分支](#%E5%88%A0%E9%99%A4%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF) - - [删除远程分支](#%E5%88%A0%E9%99%A4%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF) - - [重命名本地分支](#%E9%87%8D%E5%91%BD%E5%90%8D%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF) - - [查看标签](#%E6%9F%A5%E7%9C%8B%E6%A0%87%E7%AD%BE) - - [本地创建标签](#%E6%9C%AC%E5%9C%B0%E5%88%9B%E5%BB%BA%E6%A0%87%E7%AD%BE) - - [推送标签到远程仓库](#%E6%8E%A8%E9%80%81%E6%A0%87%E7%AD%BE%E5%88%B0%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93) - - [删除本地标签](#%E5%88%A0%E9%99%A4%E6%9C%AC%E5%9C%B0%E6%A0%87%E7%AD%BE) - - [删除远程标签](#%E5%88%A0%E9%99%A4%E8%BF%9C%E7%A8%8B%E6%A0%87%E7%AD%BE) - - [切回到某个标签](#%E5%88%87%E5%9B%9E%E5%88%B0%E6%9F%90%E4%B8%AA%E6%A0%87%E7%AD%BE) - - [放弃工作区的修改](#%E6%94%BE%E5%BC%83%E5%B7%A5%E4%BD%9C%E5%8C%BA%E7%9A%84%E4%BF%AE%E6%94%B9) - - [恢复删除的文件](#%E6%81%A2%E5%A4%8D%E5%88%A0%E9%99%A4%E7%9A%84%E6%96%87%E4%BB%B6) - - [回到某一个commit的状态,并重新增添一个commit](#%E5%9B%9E%E5%88%B0%E6%9F%90%E4%B8%80%E4%B8%AAcommit%E7%9A%84%E7%8A%B6%E6%80%81%EF%BC%8C%E5%B9%B6%E9%87%8D%E6%96%B0%E5%A2%9E%E6%B7%BB%E4%B8%80%E4%B8%AAcommit) - - [回到某个commit的状态,并删除后面的commit](#%E5%9B%9E%E5%88%B0%E6%9F%90%E4%B8%AAcommit%E7%9A%84%E7%8A%B6%E6%80%81%EF%BC%8C%E5%B9%B6%E5%88%A0%E9%99%A4%E5%90%8E%E9%9D%A2%E7%9A%84commit) - - [修改上一个commit的描述](#%E4%BF%AE%E6%94%B9%E4%B8%8A%E4%B8%80%E4%B8%AAcommit%E7%9A%84%E6%8F%8F%E8%BF%B0) - - [查看commit历史](#%E6%9F%A5%E7%9C%8Bcommit%E5%8E%86%E5%8F%B2) - - [查看某段代码是谁写的](#%E6%9F%A5%E7%9C%8B%E6%9F%90%E6%AE%B5%E4%BB%A3%E7%A0%81%E6%98%AF%E8%B0%81%E5%86%99%E7%9A%84) - - [显示本地执行过git命令](#%E6%98%BE%E7%A4%BA%E6%9C%AC%E5%9C%B0%E6%89%A7%E8%A1%8C%E8%BF%87git%E5%91%BD%E4%BB%A4) - - [修改作者名](#%E4%BF%AE%E6%94%B9%E4%BD%9C%E8%80%85%E5%90%8D) - - [修改远程仓库的url](#%E4%BF%AE%E6%94%B9%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93%E7%9A%84url) - - [增加远程仓库](#%E5%A2%9E%E5%8A%A0%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93) - - [列出所有远程仓库](#%E5%88%97%E5%87%BA%E6%89%80%E6%9C%89%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93) - - [查看两个星期内的改动](#%E6%9F%A5%E7%9C%8B%E4%B8%A4%E4%B8%AA%E6%98%9F%E6%9C%9F%E5%86%85%E7%9A%84%E6%94%B9%E5%8A%A8) - - [把A分支的某一个commit,放到B分支上](#%E6%8A%8Aa%E5%88%86%E6%94%AF%E7%9A%84%E6%9F%90%E4%B8%80%E4%B8%AAcommit%EF%BC%8C%E6%94%BE%E5%88%B0b%E5%88%86%E6%94%AF%E4%B8%8A) - - [给git命令起别名](#%E7%BB%99git%E5%91%BD%E4%BB%A4%E8%B5%B7%E5%88%AB%E5%90%8D) - - [存储当前的修改,但不用提交commit](#%E5%AD%98%E5%82%A8%E5%BD%93%E5%89%8D%E7%9A%84%E4%BF%AE%E6%94%B9%EF%BC%8C%E4%BD%86%E4%B8%8D%E7%94%A8%E6%8F%90%E4%BA%A4commit) - - [保存当前状态,包括untracked的文件](#%E4%BF%9D%E5%AD%98%E5%BD%93%E5%89%8D%E7%8A%B6%E6%80%81%EF%BC%8C%E5%8C%85%E6%8B%ACuntracked%E7%9A%84%E6%96%87%E4%BB%B6) - - [展示所有stashes](#%E5%B1%95%E7%A4%BA%E6%89%80%E6%9C%89stashes) - - [回到某个stash的状态](#%E5%9B%9E%E5%88%B0%E6%9F%90%E4%B8%AAstash%E7%9A%84%E7%8A%B6%E6%80%81) - - [回到最后一个stash的状态,并删除这个stash](#%E5%9B%9E%E5%88%B0%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AAstash%E7%9A%84%E7%8A%B6%E6%80%81%EF%BC%8C%E5%B9%B6%E5%88%A0%E9%99%A4%E8%BF%99%E4%B8%AAstash) - - [删除所有的stash](#%E5%88%A0%E9%99%A4%E6%89%80%E6%9C%89%E7%9A%84stash) - - [从stash中拿出某个文件的修改](#%E4%BB%8Estash%E4%B8%AD%E6%8B%BF%E5%87%BA%E6%9F%90%E4%B8%AA%E6%96%87%E4%BB%B6%E7%9A%84%E4%BF%AE%E6%94%B9) - - [展示所有tracked的文件](#%E5%B1%95%E7%A4%BA%E6%89%80%E6%9C%89tracked%E7%9A%84%E6%96%87%E4%BB%B6) - - [展示所有untracked的文件](#%E5%B1%95%E7%A4%BA%E6%89%80%E6%9C%89untracked%E7%9A%84%E6%96%87%E4%BB%B6) - - [展示所有忽略的文件](#%E5%B1%95%E7%A4%BA%E6%89%80%E6%9C%89%E5%BF%BD%E7%95%A5%E7%9A%84%E6%96%87%E4%BB%B6) - - [强制删除untracked的文件](#%E5%BC%BA%E5%88%B6%E5%88%A0%E9%99%A4untracked%E7%9A%84%E6%96%87%E4%BB%B6) - - [强制删除untracked的目录](#%E5%BC%BA%E5%88%B6%E5%88%A0%E9%99%A4untracked%E7%9A%84%E7%9B%AE%E5%BD%95) - - [展示简化的commit历史](#%E5%B1%95%E7%A4%BA%E7%AE%80%E5%8C%96%E7%9A%84commit%E5%8E%86%E5%8F%B2) - - [把某一个分支到导出成一个文件](#%E6%8A%8A%E6%9F%90%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF%E5%88%B0%E5%AF%BC%E5%87%BA%E6%88%90%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6) - - [从包中导入分支](#%E4%BB%8E%E5%8C%85%E4%B8%AD%E5%AF%BC%E5%85%A5%E5%88%86%E6%94%AF) - - [执行rebase之前自动stash](#%E6%89%A7%E8%A1%8Crebase%E4%B9%8B%E5%89%8D%E8%87%AA%E5%8A%A8stash) - - [从远程仓库根据ID,拉下某一状态,到本地分支](#%E4%BB%8E%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93%E6%A0%B9%E6%8D%AEid%EF%BC%8C%E6%8B%89%E4%B8%8B%E6%9F%90%E4%B8%80%E7%8A%B6%E6%80%81%EF%BC%8C%E5%88%B0%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF) - - [详细展示一行中的修改](#%E8%AF%A6%E7%BB%86%E5%B1%95%E7%A4%BA%E4%B8%80%E8%A1%8C%E4%B8%AD%E7%9A%84%E4%BF%AE%E6%94%B9) - - [清除gitignore文件中记录的文件](#%E6%B8%85%E9%99%A4gitignore%E6%96%87%E4%BB%B6%E4%B8%AD%E8%AE%B0%E5%BD%95%E7%9A%84%E6%96%87%E4%BB%B6) - - [展示所有alias和configs](#%E5%B1%95%E7%A4%BA%E6%89%80%E6%9C%89alias%E5%92%8Cconfigs) - - [展示忽略的文件](#%E5%B1%95%E7%A4%BA%E5%BF%BD%E7%95%A5%E7%9A%84%E6%96%87%E4%BB%B6) - - [commit历史中显示Branch1有的,但是Branch2没有commit](#commit%E5%8E%86%E5%8F%B2%E4%B8%AD%E6%98%BE%E7%A4%BAbranch1%E6%9C%89%E7%9A%84%EF%BC%8C%E4%BD%86%E6%98%AFbranch2%E6%B2%A1%E6%9C%89commit) - - [在commit log中显示GPG签名](#%E5%9C%A8commit-log%E4%B8%AD%E6%98%BE%E7%A4%BAgpg%E7%AD%BE%E5%90%8D) - - [删除全局设置](#%E5%88%A0%E9%99%A4%E5%85%A8%E5%B1%80%E8%AE%BE%E7%BD%AE) - - [新建并切换到新分支上,同时这个分支没有任何commit](#%E6%96%B0%E5%BB%BA%E5%B9%B6%E5%88%87%E6%8D%A2%E5%88%B0%E6%96%B0%E5%88%86%E6%94%AF%E4%B8%8A%EF%BC%8C%E5%90%8C%E6%97%B6%E8%BF%99%E4%B8%AA%E5%88%86%E6%94%AF%E6%B2%A1%E6%9C%89%E4%BB%BB%E4%BD%95commit) - - [展示任意分支某一文件的内容](#%E5%B1%95%E7%A4%BA%E4%BB%BB%E6%84%8F%E5%88%86%E6%94%AF%E6%9F%90%E4%B8%80%E6%96%87%E4%BB%B6%E7%9A%84%E5%86%85%E5%AE%B9) - - [clone下来指定的单一分支](#clone%E4%B8%8B%E6%9D%A5%E6%8C%87%E5%AE%9A%E7%9A%84%E5%8D%95%E4%B8%80%E5%88%86%E6%94%AF) - - [忽略某个文件的改动](#%E5%BF%BD%E7%95%A5%E6%9F%90%E4%B8%AA%E6%96%87%E4%BB%B6%E7%9A%84%E6%94%B9%E5%8A%A8) - - [忽略文件的权限变化](#%E5%BF%BD%E7%95%A5%E6%96%87%E4%BB%B6%E7%9A%84%E6%9D%83%E9%99%90%E5%8F%98%E5%8C%96) - - [以最后提交的顺序列出所有Git分支](#%E4%BB%A5%E6%9C%80%E5%90%8E%E6%8F%90%E4%BA%A4%E7%9A%84%E9%A1%BA%E5%BA%8F%E5%88%97%E5%87%BA%E6%89%80%E6%9C%89git%E5%88%86%E6%94%AF) - - [在commit log中查找相关内容](#%E5%9C%A8commit-log%E4%B8%AD%E6%9F%A5%E6%89%BE%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9) - - [把暂存区的指定file放到工作区中](#%E6%8A%8A%E6%9A%82%E5%AD%98%E5%8C%BA%E7%9A%84%E6%8C%87%E5%AE%9Afile%E6%94%BE%E5%88%B0%E5%B7%A5%E4%BD%9C%E5%8C%BA%E4%B8%AD) - - [强制推送](#%E5%BC%BA%E5%88%B6%E6%8E%A8%E9%80%81) - - [回退到指定版本号](#%E5%9B%9E%E9%80%80%E5%88%B0%E6%8C%87%E5%AE%9A%E7%89%88%E6%9C%AC%E5%8F%B7) - - [资料](#%E8%B5%84%E6%96%99) + - [编辑提交(editting commits)](#%E7%BC%96%E8%BE%91%E6%8F%90%E4%BA%A4editting-commits) + - [我刚才提交了什么](#%E6%88%91%E5%88%9A%E6%89%8D%E6%8F%90%E4%BA%A4%E4%BA%86%E4%BB%80%E4%B9%88) + - [我的提交信息(commit message)写错了](#%E6%88%91%E7%9A%84%E6%8F%90%E4%BA%A4%E4%BF%A1%E6%81%AFcommit-message%E5%86%99%E9%94%99%E4%BA%86) + - [我提交(commit)里的用户名和邮箱不对](#%E6%88%91%E6%8F%90%E4%BA%A4commit%E9%87%8C%E7%9A%84%E7%94%A8%E6%88%B7%E5%90%8D%E5%92%8C%E9%82%AE%E7%AE%B1%E4%B8%8D%E5%AF%B9) + - [我想从一个提交(commit)里移除一个文件](#%E6%88%91%E6%83%B3%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%8F%90%E4%BA%A4commit%E9%87%8C%E7%A7%BB%E9%99%A4%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6) + - [我想删除我的的最后一次提交(commit)](#%E6%88%91%E6%83%B3%E5%88%A0%E9%99%A4%E6%88%91%E7%9A%84%E7%9A%84%E6%9C%80%E5%90%8E%E4%B8%80%E6%AC%A1%E6%8F%90%E4%BA%A4commit) + - [删除任意提交(commit)](#%E5%88%A0%E9%99%A4%E4%BB%BB%E6%84%8F%E6%8F%90%E4%BA%A4commit) + - [我尝试推一个修正后的提交(amended commit)到远程,但是报错:](#%E6%88%91%E5%B0%9D%E8%AF%95%E6%8E%A8%E4%B8%80%E4%B8%AA%E4%BF%AE%E6%AD%A3%E5%90%8E%E7%9A%84%E6%8F%90%E4%BA%A4amended-commit%E5%88%B0%E8%BF%9C%E7%A8%8B%EF%BC%8C%E4%BD%86%E6%98%AF%E6%8A%A5%E9%94%99%EF%BC%9A) + - [我意外的做了一次硬重置(hard reset),我想找回我的内容](#%E6%88%91%E6%84%8F%E5%A4%96%E7%9A%84%E5%81%9A%E4%BA%86%E4%B8%80%E6%AC%A1%E7%A1%AC%E9%87%8D%E7%BD%AEhard-reset%EF%BC%8C%E6%88%91%E6%83%B3%E6%89%BE%E5%9B%9E%E6%88%91%E7%9A%84%E5%86%85%E5%AE%B9) + - [暂存(Staging)](#%E6%9A%82%E5%AD%98staging) + - [我需要把暂存的内容添加到上一次的提交(commit)](#%E6%88%91%E9%9C%80%E8%A6%81%E6%8A%8A%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E6%B7%BB%E5%8A%A0%E5%88%B0%E4%B8%8A%E4%B8%80%E6%AC%A1%E7%9A%84%E6%8F%90%E4%BA%A4commit) + - [我想要暂存一个新文件的一部分,而不是这个文件的全部](#%E6%88%91%E6%83%B3%E8%A6%81%E6%9A%82%E5%AD%98%E4%B8%80%E4%B8%AA%E6%96%B0%E6%96%87%E4%BB%B6%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86%EF%BC%8C%E8%80%8C%E4%B8%8D%E6%98%AF%E8%BF%99%E4%B8%AA%E6%96%87%E4%BB%B6%E7%9A%84%E5%85%A8%E9%83%A8) + - [我想把在一个文件里的变化(changes)加到两个提交(commit)里](#%E6%88%91%E6%83%B3%E6%8A%8A%E5%9C%A8%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E9%87%8C%E7%9A%84%E5%8F%98%E5%8C%96changes%E5%8A%A0%E5%88%B0%E4%B8%A4%E4%B8%AA%E6%8F%90%E4%BA%A4commit%E9%87%8C) + - [我想把暂存的内容变成未暂存,把未暂存的内容暂存起来](#%E6%88%91%E6%83%B3%E6%8A%8A%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E5%8F%98%E6%88%90%E6%9C%AA%E6%9A%82%E5%AD%98%EF%BC%8C%E6%8A%8A%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E6%9A%82%E5%AD%98%E8%B5%B7%E6%9D%A5) + - [未暂存(Unstaged)的内容](#%E6%9C%AA%E6%9A%82%E5%AD%98unstaged%E7%9A%84%E5%86%85%E5%AE%B9) + - [我想把未暂存的内容移动到一个新分支](#%E6%88%91%E6%83%B3%E6%8A%8A%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E7%A7%BB%E5%8A%A8%E5%88%B0%E4%B8%80%E4%B8%AA%E6%96%B0%E5%88%86%E6%94%AF) + - [我想把未暂存的内容移动到另一个已存在的分支](#%E6%88%91%E6%83%B3%E6%8A%8A%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E7%A7%BB%E5%8A%A8%E5%88%B0%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%B7%B2%E5%AD%98%E5%9C%A8%E7%9A%84%E5%88%86%E6%94%AF) + - [我想丢弃本地未提交的变化(uncommitted changes)](#%E6%88%91%E6%83%B3%E4%B8%A2%E5%BC%83%E6%9C%AC%E5%9C%B0%E6%9C%AA%E6%8F%90%E4%BA%A4%E7%9A%84%E5%8F%98%E5%8C%96uncommitted-changes) + - [我想丢弃某些未暂存的内容](#%E6%88%91%E6%83%B3%E4%B8%A2%E5%BC%83%E6%9F%90%E4%BA%9B%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9) + - [分支(Branches)](#%E5%88%86%E6%94%AFbranches) + - [我从错误的分支拉取了内容,或把内容拉取到了错误的分支](#%E6%88%91%E4%BB%8E%E9%94%99%E8%AF%AF%E7%9A%84%E5%88%86%E6%94%AF%E6%8B%89%E5%8F%96%E4%BA%86%E5%86%85%E5%AE%B9%EF%BC%8C%E6%88%96%E6%8A%8A%E5%86%85%E5%AE%B9%E6%8B%89%E5%8F%96%E5%88%B0%E4%BA%86%E9%94%99%E8%AF%AF%E7%9A%84%E5%88%86%E6%94%AF) + - [我想扔掉本地的提交(commit),以便我的分支与远程的保持一致](#%E6%88%91%E6%83%B3%E6%89%94%E6%8E%89%E6%9C%AC%E5%9C%B0%E7%9A%84%E6%8F%90%E4%BA%A4commit%EF%BC%8C%E4%BB%A5%E4%BE%BF%E6%88%91%E7%9A%84%E5%88%86%E6%94%AF%E4%B8%8E%E8%BF%9C%E7%A8%8B%E7%9A%84%E4%BF%9D%E6%8C%81%E4%B8%80%E8%87%B4) + - [我需要提交到一个新分支,但错误的提交到了master](#%E6%88%91%E9%9C%80%E8%A6%81%E6%8F%90%E4%BA%A4%E5%88%B0%E4%B8%80%E4%B8%AA%E6%96%B0%E5%88%86%E6%94%AF%EF%BC%8C%E4%BD%86%E9%94%99%E8%AF%AF%E7%9A%84%E6%8F%90%E4%BA%A4%E5%88%B0%E4%BA%86master) + - [我想保留来自另外一个ref-ish的整个文件](#%E6%88%91%E6%83%B3%E4%BF%9D%E7%95%99%E6%9D%A5%E8%87%AA%E5%8F%A6%E5%A4%96%E4%B8%80%E4%B8%AAref-ish%E7%9A%84%E6%95%B4%E4%B8%AA%E6%96%87%E4%BB%B6) + - [我把几个提交(commit)提交到了同一个分支,而这些提交应该分布在不同的分支里](#%E6%88%91%E6%8A%8A%E5%87%A0%E4%B8%AA%E6%8F%90%E4%BA%A4commit%E6%8F%90%E4%BA%A4%E5%88%B0%E4%BA%86%E5%90%8C%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF%EF%BC%8C%E8%80%8C%E8%BF%99%E4%BA%9B%E6%8F%90%E4%BA%A4%E5%BA%94%E8%AF%A5%E5%88%86%E5%B8%83%E5%9C%A8%E4%B8%8D%E5%90%8C%E7%9A%84%E5%88%86%E6%94%AF%E9%87%8C) + - [我想删除上游(upstream)分支被删除了的本地分支](#%E6%88%91%E6%83%B3%E5%88%A0%E9%99%A4%E4%B8%8A%E6%B8%B8upstream%E5%88%86%E6%94%AF%E8%A2%AB%E5%88%A0%E9%99%A4%E4%BA%86%E7%9A%84%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF) + - [我不小心删除了我的分支](#%E6%88%91%E4%B8%8D%E5%B0%8F%E5%BF%83%E5%88%A0%E9%99%A4%E4%BA%86%E6%88%91%E7%9A%84%E5%88%86%E6%94%AF) + - [我想删除一个分支](#%E6%88%91%E6%83%B3%E5%88%A0%E9%99%A4%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF) + - [我想从别人正在工作的远程分支签出(checkout)一个分支](#%E6%88%91%E6%83%B3%E4%BB%8E%E5%88%AB%E4%BA%BA%E6%AD%A3%E5%9C%A8%E5%B7%A5%E4%BD%9C%E7%9A%84%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF%E7%AD%BE%E5%87%BAcheckout%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF) + - [Rebasing 和合并(Merging)](#rebasing-%E5%92%8C%E5%90%88%E5%B9%B6merging) + - [我想撤销rebase/merge](#%E6%88%91%E6%83%B3%E6%92%A4%E9%94%80rebasemerge) + - [我已经rebase过, 但是我不想强推(force push)](#%E6%88%91%E5%B7%B2%E7%BB%8Frebase%E8%BF%87-%E4%BD%86%E6%98%AF%E6%88%91%E4%B8%8D%E6%83%B3%E5%BC%BA%E6%8E%A8force-push) + - [我需要组合(combine)几个提交(commit)](#%E6%88%91%E9%9C%80%E8%A6%81%E7%BB%84%E5%90%88combine%E5%87%A0%E4%B8%AA%E6%8F%90%E4%BA%A4commit) + - [安全合并(merging)策略](#%E5%AE%89%E5%85%A8%E5%90%88%E5%B9%B6merging%E7%AD%96%E7%95%A5) + - [我需要将一个分支合并成一个提交(commit)](#%E6%88%91%E9%9C%80%E8%A6%81%E5%B0%86%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF%E5%90%88%E5%B9%B6%E6%88%90%E4%B8%80%E4%B8%AA%E6%8F%90%E4%BA%A4commit) + - [我只想组合(combine)未推的提交(unpushed commit)](#%E6%88%91%E5%8F%AA%E6%83%B3%E7%BB%84%E5%90%88combine%E6%9C%AA%E6%8E%A8%E7%9A%84%E6%8F%90%E4%BA%A4unpushed-commit) + - [检查是否分支上的所有提交(commit)都合并(merge)过了](#%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%88%86%E6%94%AF%E4%B8%8A%E7%9A%84%E6%89%80%E6%9C%89%E6%8F%90%E4%BA%A4commit%E9%83%BD%E5%90%88%E5%B9%B6merge%E8%BF%87%E4%BA%86) + - [交互式rebase(interactive rebase)可能出现的问题](#%E4%BA%A4%E4%BA%92%E5%BC%8Frebaseinteractive-rebase%E5%8F%AF%E8%83%BD%E5%87%BA%E7%8E%B0%E7%9A%84%E9%97%AE%E9%A2%98) + - [这个rebase 编辑屏幕出现'noop'](#%E8%BF%99%E4%B8%AArebase-%E7%BC%96%E8%BE%91%E5%B1%8F%E5%B9%95%E5%87%BA%E7%8E%B0noop) + - [有冲突的情况](#%E6%9C%89%E5%86%B2%E7%AA%81%E7%9A%84%E6%83%85%E5%86%B5) + - [杂项(Miscellaneous Objects)](#%E6%9D%82%E9%A1%B9miscellaneous-objects) + - [克隆所有子模块](#%E5%85%8B%E9%9A%86%E6%89%80%E6%9C%89%E5%AD%90%E6%A8%A1%E5%9D%97) + - [删除标签(tag)](#%E5%88%A0%E9%99%A4%E6%A0%87%E7%AD%BEtag) + - [恢复已删除标签(tag)](#%E6%81%A2%E5%A4%8D%E5%B7%B2%E5%88%A0%E9%99%A4%E6%A0%87%E7%AD%BEtag) + - [已删除补丁(patch)](#%E5%B7%B2%E5%88%A0%E9%99%A4%E8%A1%A5%E4%B8%81patch) + - [跟踪文件(Tracking Files)](#%E8%B7%9F%E8%B8%AA%E6%96%87%E4%BB%B6tracking-files) + - [我只想改变一个文件名字的大小写,而不修改内容](#%E6%88%91%E5%8F%AA%E6%83%B3%E6%94%B9%E5%8F%98%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E5%90%8D%E5%AD%97%E7%9A%84%E5%A4%A7%E5%B0%8F%E5%86%99%EF%BC%8C%E8%80%8C%E4%B8%8D%E4%BF%AE%E6%94%B9%E5%86%85%E5%AE%B9) + - [我想从Git删除一个文件,但保留该文件](#%E6%88%91%E6%83%B3%E4%BB%8Egit%E5%88%A0%E9%99%A4%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%EF%BC%8C%E4%BD%86%E4%BF%9D%E7%95%99%E8%AF%A5%E6%96%87%E4%BB%B6) + - [配置(Configuration)](#%E9%85%8D%E7%BD%AEconfiguration) + - [我想给一些Git命令添加别名(alias)](#%E6%88%91%E6%83%B3%E7%BB%99%E4%B8%80%E4%BA%9Bgit%E5%91%BD%E4%BB%A4%E6%B7%BB%E5%8A%A0%E5%88%AB%E5%90%8Dalias) + - [我想缓存一个仓库(repository)的用户名和密码](#%E6%88%91%E6%83%B3%E7%BC%93%E5%AD%98%E4%B8%80%E4%B8%AA%E4%BB%93%E5%BA%93repository%E7%9A%84%E7%94%A8%E6%88%B7%E5%90%8D%E5%92%8C%E5%AF%86%E7%A0%81) + - [我不知道我做错了些什么](#%E6%88%91%E4%B8%8D%E7%9F%A5%E9%81%93%E6%88%91%E5%81%9A%E9%94%99%E4%BA%86%E4%BA%9B%E4%BB%80%E4%B9%88) + - [资源](#%E8%B5%84%E6%BA%90) # Git 常见问题 -## 帮助 +## 编辑提交(editting commits) -### 展示帮助信息 +### 我刚才提交了什么 + +如果你用 `git commit -a` 提交了一次变化(changes),而你又不确定到底这次提交了哪些内容。 你就可以用下面的命令显示当前`HEAD`上的最近一次的提交(commit): ```sh -git help -g +(master)$ git show ``` -### 回到远程仓库的状态 +或者 -抛弃本地所有的修改,回到远程仓库的状态。 ```sh -git fetch --all && git reset --hard origin/master +$ git log -n1 -p ``` -### 重设第一个commit +### 我的提交信息(commit message)写错了 + +如果你的提交信息(commit message)写错了且这次提交(commit)还没有推(push), 你可以通过下面的方法来修改提交信息(commit message): -也就是把所有的改动都重新放回工作区,并**清空所有的commit**,这样就可以重新提交第一个commit了 ```sh -git update-ref -d HEAD +$ git commit --amend ``` -### 展示工作区和暂存区的不同 +这会打开你的默认编辑器, 在这里你可以编辑信息. 另一方面, 你也可以用一条命令一次完成: -输出**工作区**和**暂存区**的different(不同)。 ```sh -git diff +$ git commit --amend -m 'xxxxxxx' ``` -还可以展示本地仓库中任意两个commit之间的文件变动: +如果你已经推(push)了这次提交(commit), 你可以修改这次提交(commit)然后强推(force push), 但是不推荐这么做。 + +### 我提交(commit)里的用户名和邮箱不对 + +如果这只是单个提交(commit),修改它: + ```sh -git diff +$ git commit --amend --author "New Authorname " ``` -### 展示暂存区和最近版本的不同 +如果你需要修改所有历史, 参考 'git filter-branch'的指南页. + +### 我想从一个提交(commit)里移除一个文件 + +通过下面的方法,从一个提交(commit)里移除一个文件: -输出**暂存区**和本地最近的版本(commit)的different(不同)。 ```sh -git diff --cached +$ git checkout HEAD^ myfile +$ git add -A +$ git commit --amend ``` -### 展示暂存区、工作区和最近版本的不同 +这将非常有用,当你有一个开放的补丁(open patch),你往上面提交了一个不必要的文件,你需要强推(force push)去更新这个远程补丁。 + +### 我想删除我的的最后一次提交(commit) + +如果你需要删除推了的提交(pushed commits),你可以使用下面的方法。可是,这会不可逆的改变你的历史,也会搞乱那些已经从该仓库拉取(pulled)了的人的历史。简而言之,如果你不是很确定,千万不要这么做。 -输出**工作区**、**暂存区** 和本地最近的版本(commit)的different(不同)。 ```sh -git diff HEAD +$ git reset HEAD^ --hard +$ git push -f [remote] [branch] ``` -### 快速切换分支 +如果你还没有推到远程, 把Git重置(reset)到你最后一次提交前的状态就可以了(同时保存暂存的变化): + +``` +(my-branch*)$ git reset --soft HEAD@{1} -```sh -git checkout - ``` -### 删除已经合并到master的分支 +这只能在没有推送之前有用. 如果你已经推了, 唯一安全能做的是 `git revert SHAofBadCommit`, 那会创建一个新的提交(commit)用于撤消前一个提交的所有变化(changes); 或者, 如果你推的这个分支是rebase-safe的 (例如: 其它开发者不会从这个分支拉), 只需要使用 `git push -f`; 更多, 请参考 [the above section](#deleteremove-last-pushed-commit)。 + +### 删除任意提交(commit) + +同样的警告:不到万不得已的时候不要这么做. ```sh -git branch --merged master | grep -v '^\*\| master' | xargs -n 1 git branch -d +$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT +$ git push -f [remote] [branch] ``` -### 展示本地分支关联远程仓库的情况 +或者做一个 [交互式rebase](#interactive-rebase) 删除那些你想要删除的提交(commit)里所对应的行。 + +### 我尝试推一个修正后的提交(amended commit)到远程,但是报错: ```sh -git branch -vv +To https://github.com/yourusername/repo.git +! [rejected] mybranch -> mybranch (non-fast-forward) +error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git' +hint: Updates were rejected because the tip of your current branch is behind +hint: its remote counterpart. Integrate the remote changes (e.g. +hint: 'git pull ...') before pushing again. +hint: See the 'Note about fast-forwards' in 'git push --help' for details. ``` -### 关联远程分支 +注意, rebasing(见下面)和修正(amending)会用一个**新的提交(commit)代替旧的**, 所以如果之前你已经往远程仓库上推过一次修正前的提交(commit),那你现在就必须强推(force push) (`-f`)。 注意 – *总是* 确保你指明一个分支! -关联之后,`git branch -vv`就可以展示关联的远程分支名了,同时推送到远程仓库直接:`git push`,不需要指定远程仓库了。 ```sh -git branch -u origin/mybranch +(my-branch)$ git push origin mybranch -f ``` -或者在push时加上`-u`参数 +一般来说, **要避免强推**. 最好是创建和推(push)一个新的提交(commit),而不是强推一个修正后的提交。后者会使那些与该分支或该分支的子分支工作的开发者,在源历史中产生冲突。 + +### 我意外的做了一次硬重置(hard reset),我想找回我的内容 + +如果你意外的做了 `git reset --hard`, 你通常能找回你的提交(commit), 因为Git对每件事都会有日志,且都会保存几天。 + ```sh -git push origin/mybranch -u +(master)$ git reflog ``` -### 列出所有远程分支 +你将会看到一个你过去提交(commit)的列表, 和一个重置的提交。 选择你想要回到的提交(commit)的SHA,再重置一次: --r参数相当于:remote ```sh -git branch -r +(master)$ git reset --hard SHA1234 ``` -### 列出本地和远程分支 +这样就完成了。 + +## 暂存(Staging) + +### 我需要把暂存的内容添加到上一次的提交(commit) --a参数相当于:all ```sh -git branch -a +(my-branch*)$ git commit --amend + ``` -### 创建并切换到本地分支 +### 我想要暂存一个新文件的一部分,而不是这个文件的全部 + +一般来说, 如果你想暂存一个文件的一部分, 你可这样做: ```sh -git checkout -b +$ git add --patch filename.x ``` -### 创建并切换到远程分支 +`-p` 简写。这会打开交互模式, 你将能够用 `s` 选项来分隔提交(commit); 然而, 如果这个文件是新的, 会没有这个选择, 添加一个新文件时, 这样做: ```sh -git checkout -b origin/ +$ git add -N filename.x ``` -### 删除本地分支 +然后, 你需要用 `e` 选项来手动选择需要添加的行,执行 `git diff --cached` 将会显示哪些行暂存了哪些行只是保存在本地了。 + +### 我想把在一个文件里的变化(changes)加到两个提交(commit)里 + +`git add` 会把整个文件加入到一个提交. `git add -p` 允许交互式的选择你想要提交的部分. + +### 我想把暂存的内容变成未暂存,把未暂存的内容暂存起来 + +这个有点困难, 我能想到的最好的方法是先stash未暂存的内容, 然后重置(reset),再pop第一步stashed的内容, 最后再add它们。 ```sh -git branch -d +$ git stash -k +$ git reset --hard +$ git stash pop +$ git add -A ``` -### 删除远程分支 +## 未暂存(Unstaged)的内容 + +### 我想把未暂存的内容移动到一个新分支 ```sh -git push origin --delete +$ git checkout -b my-branch ``` -或者 +### 我想把未暂存的内容移动到另一个已存在的分支 + ```sh -git push origin : +$ git stash +$ git checkout my-branch +$ git stash pop ``` -### 重命名本地分支 +### 我想丢弃本地未提交的变化(uncommitted changes) + +如果你只是想重置源(origin)和你本地(local)之间的一些提交(commit),你可以: ```sh -git branch -m +# one commit +(my-branch)$ git reset --hard HEAD^ +# two commits +(my-branch)$ git reset --hard HEAD^^ +# four commits +(my-branch)$ git reset --hard HEAD~4 +# or +(master)$ git checkout -f ``` -### 查看标签 +重置某个特殊的文件, 你可以用文件名做为参数: +```sh +$ git reset filename ``` -git tag -``` -展示当前分支的最近的tag +### 我想丢弃某些未暂存的内容 + +如果你想丢弃工作拷贝中的一部分内容,而不是全部。 + +签出(checkout)不需要的内容,保留需要的。 + ```sh -git describe --tags --abbrev=0 +$ git checkout -p +# Answer y to all of the snippets you want to drop ``` -### 本地创建标签 +另外一个方法是使用 `stash`, Stash所有要保留下的内容, 重置工作拷贝, 重新应用保留的部分。 ```sh -git tag +$ git stash -p +# Select all of the snippets you want to save +$ git reset --hard +$ git stash pop ``` -默认tag是打在最近的一次commit上,如果需要指定commit打tag: +或者, stash 你不需要的部分, 然后stash drop。 + ```sh -$ git tag -a -m "v1.0 发布(描述)" +$ git stash -p +# Select all of the snippets you don't want to save +$ git stash drop ``` -### 推送标签到远程仓库 +## 分支(Branches) + +### 我从错误的分支拉取了内容,或把内容拉取到了错误的分支 + +这是另外一种使用 `git reflog` 情况,找到在这次错误拉(pull) 之前HEAD的指向。 -首先要保证本地创建好了标签才可以推送标签到远程仓库: ```sh -git push origin +(master)$ git reflog +ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward +c5bc55a HEAD@{1}: checkout: checkout message goes here ``` -一次性推送所有标签,同步到远程仓库: -``` -git push origin --tags +重置分支到你所需的提交(desired commit): + +```sh +$ git reset --hard c5bc55a ``` -### 删除本地标签 +完成。 + +### 我想扔掉本地的提交(commit),以便我的分支与远程的保持一致 + +先确认你没有推(push)你的内容到远程。 + +`git status` 会显示你领先(ahead)源(origin)多少个提交: ```sh -git tag -d +(my-branch)$ git status +# On branch my-branch +# Your branch is ahead of 'origin/my-branch' by 2 commits. +# (use "git push" to publish your local commits) +# ``` -### 删除远程标签 +一种方法是: -删除远程标签需要**先删除本地标签**,再执行下面的命令: ```sh -git push origin :refs/tags/ +(master)$ git reset --hard origin/my-branch ``` -### 切回到某个标签 +### 我需要提交到一个新分支,但错误的提交到了master + +在master下创建一个新分支,不切换到新分支,仍在master下: -一般上线之前都会打tag,就是为了防止上线后出现问题,方便快速回退到上一版本。下面的命令是回到某一标签下的状态: ```sh -git checkout -b branch_name tag_name +(master)$ git branch my-branch ``` -### 放弃工作区的修改 +把master分支重置到前一个提交: ```sh -git checkout +(master)$ git reset --hard HEAD^ ``` -放弃所有修改: +`HEAD^` 是 `HEAD^1` 的简写,你可以通过指定要设置的`HEAD`来进一步重置。 + +或者, 如果你不想使用 `HEAD^`, 找到你想重置到的提交(commit)的hash(`git log` 能够完成), 然后重置到这个hash。 使用`git push` 同步内容到远程。 + +例如, master分支想重置到的提交的hash为`a13b85e`: + ```sh -git checkout . +(master)$ git reset --hard a13b85e +HEAD is now at a13b85e ``` -### 恢复删除的文件 +签出(checkout)刚才新建的分支继续工作: ```sh -git rev-list -n 1 HEAD -- #得到 deleting_commit - -git checkout ^ -- #回到删除文件 deleting_commit 之前的状态 +(master)$ git checkout my-branch ``` -### 回到某一个commit的状态,并重新增添一个commit +### 我想保留来自另外一个ref-ish的整个文件 + +假设你正在做一个原型方案(原文为working spike (see note)), 有成百的内容,每个都工作得很好。现在, 你提交到了一个分支,保存工作内容: ```sh -git revert +(solution)$ git add -A && git commit -m "Adding all changes from this spike into one big commit." ``` -### 回到某个commit的状态,并删除后面的commit +当你想要把它放到一个分支里 (可能是`feature`, 或者 `develop`), 你关心是保持整个文件的完整,你想要一个大的提交分隔成比较小。 -和revert的区别:reset命令会抹去某个commit id之后的所有commit -```sh -git reset #默认就是-mixed参数。 +假设你有: -git reset –mixed HEAD^ #回退至上个版本,它将重置HEAD到另外一个commit,并且重置暂存区以便和HEAD相匹配,但是也到此为止。工作区不会被更改。 + * 分支 `solution`, 拥有原型方案, 领先 `develop` 分支。 + * 分支 `develop`, 在这里你应用原型方案的一些内容。 -git reset –soft HEAD~3 #回退至三个版本之前,只回退了commit的信息,暂存区和工作区与回退之前保持一致。如果还要提交,直接commit即可 +我去可以通过把内容拿到你的分支里,来解决这个问题: -git reset –hard #彻底回退到指定commit-id的状态,暂存区和工作区也会变为指定commit-id版本的内容 +```sh +(develop)$ git checkout solution -- file1.txt ``` -### 修改上一个commit的描述 +这会把这个文件内容从分支 `solution` 拿到分支 `develop` 里来: ```sh -git commit --amend +# On branch develop +# Your branch is up-to-date with 'origin/develop'. +# Changes to be committed: +# (use "git reset HEAD ..." to unstage) +# +# modified: file1.txt ``` -### 查看commit历史 +然后, 正常提交。 + +Note: Spike solutions are made to analyze or solve the problem. These solutions are used for estimation and discarded once everyone gets clear visualization of the problem. ~ [Wikipedia](https://en.wikipedia.org/wiki/Extreme_programming_practices). + +### 我把几个提交(commit)提交到了同一个分支,而这些提交应该分布在不同的分支里 + +假设你有一个`master`分支, 执行`git log`, 你看到你做过两次提交: ```sh -git log +(master)$ git log + +commit e3851e817c451cc36f2e6f3049db528415e3c114 +Author: Alex Lee +Date: Tue Jul 22 15:39:27 2014 -0400 + + Bug #21 - Added CSRF protection + +commit 5ea51731d150f7ddc4a365437931cd8be3bf3131 +Author: Alex Lee +Date: Tue Jul 22 15:39:12 2014 -0400 + + Bug #14 - Fixed spacing on title + +commit a13b85e984171c6e2a1729bb061994525f626d14 +Author: Aki Rose +Date: Tue Jul 21 01:12:48 2014 -0400 + + First commit ``` -### 查看某段代码是谁写的 +让我们用提交hash(commit hash)标记bug (`e3851e8` for #21, `5ea5173` for #14). + +首先, 我们把`master`分支重置到正确的提交(`a13b85e`): -blame的意思为‘责怪’,你懂的。 ```sh -git blame +(master)$ git reset --hard a13b85e +HEAD is now at a13b85e ``` -### 显示本地执行过git命令 +现在, 我们对 bug #21 创建一个新的分支: -就像shell的history一样 +```sh +(master)$ git checkout -b 21 +(21)$ ``` -git reflog + +接着, 我们用 *cherry-pick* 把对bug #21的提交放入当前分支。 这意味着我们将应用(apply)这个提交(commit),仅仅这一个提交(commit),直接在HEAD上面。 + +```sh +(21)$ git cherry-pick e3851e8 ``` -### 修改作者名 +这时候, 这里可能会产生冲突, 参见[交互式 rebasing 章](#interactive-rebase) [**冲突节**](#merge-conflict) 解决冲突. + +再者, 我们为bug #14 创建一个新的分支, 也基于`master`分支 ```sh -git commit --amend --author='Author Name ' +(21)$ git checkout master +(master)$ git checkout -b 14 +(14)$ ``` -### 修改远程仓库的url +最后, 为 bug #14 执行 `cherry-pick`: ```sh -git remote set-url origin +(14)$ git cherry-pick 5ea5173 ``` -### 增加远程仓库 +### 我想删除上游(upstream)分支被删除了的本地分支 +一旦你在github 上面合并(merge)了一个pull request, 你就可以删除你fork里被合并的分支。 如果你不准备继续在这个分支里工作, 删除这个分支的本地拷贝会更干净,使你不会陷入工作分支和一堆陈旧分支的混乱之中。 ```sh -git remote add origin +$ git fetch -p ``` -### 列出所有远程仓库 +### 我不小心删除了我的分支 + +如果你定期推送到远程, 多数情况下应该是安全的,但有些时候还是可能删除了还没有推到远程的分支。 让我们先创建一个分支和一个新的文件: ```sh -git remote +(master)$ git checkout -b my-branch +(my-branch)$ git branch +(my-branch)$ touch foo.txt +(my-branch)$ ls +README.md foo.txt ``` -### 查看两个星期内的改动 +添加文件并做一次提交 ```sh -git whatchanged --since='2 weeks ago' +(my-branch)$ git add . +(my-branch)$ git commit -m 'foo.txt added' +(my-branch)$ foo.txt added + 1 files changed, 1 insertions(+) + create mode 100644 foo.txt +(my-branch)$ git log + +commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012 +Author: siemiatj +Date: Wed Jul 30 00:34:10 2014 +0200 + + foo.txt added + +commit 69204cdf0acbab201619d95ad8295928e7f411d5 +Author: Kate Hudson +Date: Tue Jul 29 13:14:46 2014 -0400 + + Fixes #6: Force pushing after amending commits ``` -### 把A分支的某一个commit,放到B分支上 +现在我们切回到主(master)分支,‘不小心的’删除`my-branch`分支 -这个过程需要`cherry-pick`命令,[参考](http://sg552.iteye.com/blog/1300713#bc2367928) ```sh -git checkout && git cherry-pick +(my-branch)$ git checkout master +Switched to branch 'master' +Your branch is up-to-date with 'origin/master'. +(master)$ git branch -D my-branch +Deleted branch my-branch (was 4e3cd85). +(master)$ echo oh noes, deleted my branch! +oh noes, deleted my branch! ``` -### 给git命令起别名 +在这时候你应该想起了`reflog`, 一个升级版的日志,它存储了仓库(repo)里面所有动作的历史。 + +``` +(master)$ git reflog +69204cd HEAD@{0}: checkout: moving from my-branch to master +4e3cd85 HEAD@{1}: commit: foo.txt added +69204cd HEAD@{2}: checkout: moving from master to my-branch +``` -简化命令 +正如你所见,我们有一个来自删除分支的提交hash(commit hash),接下来看看是否能恢复删除了的分支。 ```sh -git config --global alias. +(master)$ git checkout -b my-branch-help +Switched to a new branch 'my-branch-help' +(my-branch-help)$ git reset --hard 4e3cd85 +HEAD is now at 4e3cd85 foo.txt added +(my-branch-help)$ ls +README.md foo.txt +``` -比如:git status 改成 git st,这样可以简化命令 +看! 我们把删除的文件找回来了。 Git的 `reflog` 在rebasing出错的时候也是同样有用的。 -git config --global alias.st status -``` +### 我想删除一个分支 -### 存储当前的修改,但不用提交commit +删除一个远程分支: -详解可以参考[廖雪峰老师的git教程](http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/00137602359178794d966923e5c4134bc8bf98dfb03aea3000) ```sh -git stash +(master)$ git push origin --delete my-branch ``` -### 保存当前状态,包括untracked的文件 +你也可以: -untracked文件:新建的文件 ```sh -git stash -u +(master)$ git push origin :my-branch ``` -### 展示所有stashes +删除一个本地分支: ```sh -git stash list +(master)$ git branch -D my-branch ``` -### 回到某个stash的状态 +### 我想从别人正在工作的远程分支签出(checkout)一个分支 + +首先, 从远程拉取(fetch) 所有分支: ```sh -git stash apply +(master)$ git fetch --all ``` -### 回到最后一个stash的状态,并删除这个stash +假设你想要从远程的`daves`分支签出到本地的`daves` ```sh -git stash pop +(master)$ git checkout --track origin/daves +Branch daves set up to track remote branch daves from origin. +Switched to a new branch 'daves' ``` -### 删除所有的stash +(`--track` 是 `git checkout -b [branch] [remotename]/[branch]` 的简写) + +这样就得到了一个`daves`分支的本地拷贝, 任何推过(pushed)的更新,远程都能看到. + +## Rebasing 和合并(Merging) + +### 我想撤销rebase/merge + +你可以合并(merge)或rebase了一个错误的分支, 或者完成不了一个进行中的rebase/merge。 Git 在进行危险操作的时候会把原始的HEAD保存在一个叫ORIG_HEAD的变量里, 所以要把分支恢复到rebase/merge前的状态是很容易的。 ```sh -git stash clear +(my-branch)$ git reset --hard ORIG_HEAD ``` -### 从stash中拿出某个文件的修改 +### 我已经rebase过, 但是我不想强推(force push) + +不幸的是,如果你想把这些变化(changes)反应到远程分支上,你就必须得强推(force push)。 是因你快进(Fast forward)了提交,改变了Git历史, 远程分支不会接受变化(changes),除非强推(force push)。这就是许多人使用 merge 工作流, 而不是 rebasing 工作流的主要原因之一, 开发者的强推(force push)会使大的团队陷入麻烦。使用时需要注意,一种安全使用 rebase 的方法是,不要把你的变化(changes)反映到远程分支上, 而是按下面的做: ```sh -git checkout -- +(master)$ git checkout my-branch +(my-branch)$ git rebase -i master +(my-branch)$ git checkout master +(master)$ git merge --ff-only my-branch ``` -### 展示所有tracked的文件 +更多, 参见 [this SO thread](http://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push). + +### 我需要组合(combine)几个提交(commit) + +假设你的工作分支将会做对于 `master` 的pull-request。 一般情况下你不关心提交(commit)的时间戳,只想组合 *所有* 提交(commit) 到一个单独的里面, 然后重置(reset)重提交(recommit)。 确保主(master)分支是最新的和你的变化都已经提交了, 然后: ```sh -git ls-files -t +(my-branch)$ git reset --soft master +(my-branch)$ git commit -am "New awesome feature" ``` -### 展示所有untracked的文件 +如果你想要更多的控制, 想要保留时间戳, 你需要做交互式rebase (interactive rebase): ```sh -git ls-files --others +(my-branch)$ git rebase -i master ``` -### 展示所有忽略的文件 +如果没有相对的其它分支, 你将不得不相对自己的`HEAD` 进行 rebase。 例如:你想组合最近的两次提交(commit), 你将相对于`HEAD~2` 进行rebase, 组合最近3次提交(commit), 相对于`HEAD~3`, 等等。 ```sh -git ls-files --others -i --exclude-standard +(master)$ git rebase -i HEAD~2 ``` -### 强制删除untracked的文件 +在你执行了交互式 rebase的命令(interactive rebase command)后, 你将在你的编辑器里看到类似下面的内容: -可以用来删除新建的文件。如果不指定文件文件名,则清空所有工作的untracked文件。`clean`命令,**注意两点**: -1. clean后,删除的文件无法找回 -2. 不会影响tracked的文件的改动,只会删除untracked的文件 +```vim +pick a9c8a1d Some refactoring +pick 01b2fd8 New awesome feature +pick b729ad5 fixup +pick e3851e8 another fix -```sh -git clean -f +# Rebase 8074d12..b729ad5 onto 8074d12 +# +# 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 +# +# 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 ``` -### 强制删除untracked的目录 +所有以 `#` 开头的行都是注释, 不会影响 rebase. -可以用来删除新建的目录,**注意**:这个命令也可以用来删除untracked的文件。详情见上一条 +然后,你可以用任何上面命令列表的命令替换 `pick`, 你也可以通过删除对应的行来删除一个提交(commit)。 -```sh -git clean -df +例如, 如果你想 **单独保留最旧(first)的提交(commit),组合所有剩下的到第二个里面**, 你就应该编辑第二个提交(commit)后面的每个提交(commit) 前的单词为 `f`: + +```vim +pick a9c8a1d Some refactoring +pick 01b2fd8 New awesome feature +f b729ad5 fixup +f e3851e8 another fix ``` -### 展示简化的commit历史 +如果你想组合这些提交(commit) **并重命名这个提交(commit)**, 你应该在第二个提交(commit)旁边添加一个`r`,或者更简单的用`s` 替代 `f`: + +```vim +pick a9c8a1d Some refactoring +pick 01b2fd8 New awesome feature +s b729ad5 fixup +s e3851e8 another fix +``` + +你可以在接下来弹出的文本提示框里重命名提交(commit)。 + +```vim +Newer, awesomer features + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# rebase in progress; onto 8074d12 +# You are currently editing a commit while rebasing branch 'master' on '8074d12'. +# +# Changes to be committed: +# modified: README.md +# -```sh -git log --pretty=oneline --graph --decorate --all ``` -### 把某一个分支到导出成一个文件 +如果成功了, 你应该看到类似下面的内容: ```sh -git bundle create +(master)$ Successfully rebased and updated refs/heads/master. ``` -### 从包中导入分支 +#### 安全合并(merging)策略 +`--no-commit` 执行合并(merge)但不自动提交, 给用户在做提交前检查和修改的机会。 `no-ff` 会为特性分支(feature branch)的存在过留下证据, 保持项目历史一致。 -新建一个分支,分支内容就是上面`git bundle create`命令导出的内容 ```sh -git clone repo.bundle -b +(master)$ git merge --no-ff --no-commit my-branch ``` -### 执行rebase之前自动stash +#### 我需要将一个分支合并成一个提交(commit) ```sh -git rebase --autostash +(master)$ git merge --squash my-branch ``` -### 从远程仓库根据ID,拉下某一状态,到本地分支 +#### 我只想组合(combine)未推的提交(unpushed commit) + +有时候,在将数据推向上游之前,你有几个正在进行的工作提交(commit)。这时候不希望把已经推(push)过的组合进来,因为其他人可能已经有提交(commit)引用它们了。 ```sh -git fetch origin pull//head: +(master)$ git rebase -i @{u} ``` -### 详细展示一行中的修改 +这会产生一次交互式的rebase(interactive rebase), 只会列出没有推(push)的提交(commit), 在这个列表时进行reorder/fix/squash 都是安全的。 + +### 检查是否分支上的所有提交(commit)都合并(merge)过了 + +检查一个分支上的所有提交(commit)是否都已经合并(merge)到了其它分支, 你应该在这些分支的head(或任何 commits)之间做一次diff: ```sh -git diff --word-diff +(master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll ``` -### 清除gitignore文件中记录的文件 +这会告诉你在一个分支里有而另一个分支没有的所有提交(commit), 和分支之间不共享的提交(commit)的列表。 另一个做法可以是: ```sh -git clean -X -f +(master)$ git log master ^feature/120-on-scroll --no-merges ``` -### 展示所有alias和configs +### 交互式rebase(interactive rebase)可能出现的问题 -**注意:** config分为:当前目录(local)和全局(golbal)的config,默认为当前目录的config +#### 这个rebase 编辑屏幕出现'noop' -```sh -git config --local --list (当前目录) -git config --global --list (全局) +如果你看到的是这样: ``` +noop +``` + +这意味着你rebase的分支和当前分支在同一个提交(commit)上, 或者 *领先(ahead)* 当前分支。 你可以尝试: + +* 检查确保主(master)分支没有问题 +* rebase `HEAD~2` 或者更早 + +#### 有冲突的情况 -### 展示忽略的文件 +如果你不能成功的完成rebase, 你可能必须要解决冲突。 + +首先执行 `git status` 找出哪些文件有冲突: ```sh -git status --ignored +(my-branch)$ git status +On branch my-branch +Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git checkout -- ..." to discard changes in working directory) + + modified: README.md ``` -### commit历史中显示Branch1有的,但是Branch2没有commit +在这个例子里面, `README.md` 有冲突。 打开这个文件找到类似下面的内容: -```sh -git log Branch1 ^Branch2 +```vim + <<<<<<< HEAD + some code + ========= + some code + >>>>>>> new-commit ``` -### 在commit log中显示GPG签名 +你需要解决新提交的代码(示例里, 从中间`==`线到`new-commit`的地方)与`HEAD` 之间不一样的地方. + +有时候这些合并非常复杂,你应该使用可视化的差异编辑器(visual diff editor): ```sh -git log --show-signature +(master*)$ git mergetool -t opendiff ``` -### 删除全局设置 +在你解决完所有冲突和测试过后, `git add` 变化了的(changed)文件, 然后用`git rebase --continue` 继续rebase。 ```sh -git config --global --unset +(my-branch)$ git add README.md +(my-branch)$ git rebase --continue ``` -### 新建并切换到新分支上,同时这个分支没有任何commit +如果在解决完所有的冲突过后,得到了与提交前一样的结果, 可以执行`git rebase --skip`。 + +任何时候你想结束整个rebase 过程,回来rebase前的分支状态, 你可以做: -相当于保存修改,但是重写commit历史 ```sh -git checkout --orphan +(my-branch)$ git rebase --abort ``` -### 展示任意分支某一文件的内容 +## 杂项(Miscellaneous Objects) + +### 克隆所有子模块 ```sh -git show : +$ git clone --recursive git://github.com/foo/bar.git ``` -### clone下来指定的单一分支 +如果已经克隆了: ```sh -git clone -b --single-branch https://github.com/user/repo.git +$ git submodule update --init --recursive ``` -### 忽略某个文件的改动 +### 删除标签(tag) -关闭 track 指定文件的改动,也就是 Git 将不会在记录这个文件的改动 +```sh +$ git tag -d +$ git push :refs/tags/ ``` -git update-index --assume-unchanged path/to/file + +### 恢复已删除标签(tag) + +如果你想恢复一个已删除标签(tag), 可以按照下面的步骤: 首先, 需要找到无法访问的标签(unreachable tag): + +```sh +$ git fsck --unreachable | grep tag ``` -恢复 track 指定文件的改动 +记下这个标签(tag)的hash,然后用Git的 [update-ref](http://git-scm.com/docs/git-update-ref): + +```sh +$ git update-ref refs/tags/ ``` -git update-index --no-assume-unchanged path/to/file + +这时你的标签(tag)应该已经恢复了。 + +### 已删除补丁(patch) + +如果某人在 GitHub 上给你发了一个pull request, 但是然后他删除了他自己的原始 fork, 你将没法克隆他们的提交(commit)或使用 `git am`。在这种情况下, 最好手动的查看他们的提交(commit),并把它们拷贝到一个本地新分支,然后做提交。 + +做完提交后, 再修改作者,参见[变更作者](#commit-wrong-author)。 然后, 应用变化, 再发起一个新的pull request。 + +## 跟踪文件(Tracking Files) + +### 我只想改变一个文件名字的大小写,而不修改内容 + +```sh +(master)$ git mv --force myfile MyFile ``` -### 忽略文件的权限变化 +### 我想从Git删除一个文件,但保留该文件 -不再将文件的权限变化视作改动 ```sh -git config core.fileMode false +(master)$ git rm --cached log.txt ``` -### 以最后提交的顺序列出所有Git分支 +## 配置(Configuration) -最新的放在最上面 +### 我想给一些Git命令添加别名(alias) -```sh -git for-each-ref --sort=-committerdate --format='%(refname:short)' refs/heads/ +在 OS X 和 Linux 下, 你的 Git的配置文件储存在 ```~/.gitconfig```。我在```[alias]``` 部分添加了一些快捷别名(和一些我容易拼写错误的),如下: + +```vim +[alias] + a = add + amend = commit --amend + c = commit + ca = commit --amend + ci = commit -a + co = checkout + d = diff + dc = diff --changed + ds = diff --staged + f = fetch + loll = log --graph --decorate --pretty=oneline --abbrev-commit + m = merge + one = log --pretty=oneline + outstanding = rebase -i @{u} + s = status + unpushed = log @{u} + wc = whatchanged + wip = rebase -i @{u} + zap = fetch -p ``` -### 在commit log中查找相关内容 +### 我想缓存一个仓库(repository)的用户名和密码 -通过grep查找,given-text:所需要查找的字段 +你可能有一个仓库需要授权,这时你可以缓存用户名和密码,而不用每次推/拉(push/pull)的时候都输入,Credential helper能帮你。 ```sh -git log --all --grep='' +$ git config --global credential.helper cache +# Set git to use the credential memory cache ``` -### 把暂存区的指定file放到工作区中 - -不添加参数,默认是-mixed ```sh -git reset +$ git config --global credential.helper 'cache --timeout=3600' +# Set the cache to timeout after 1 hour (setting is in seconds) ``` -### 强制推送 +## 我不知道我做错了些什么 + +你把事情搞砸了:你 `重置(reset)` 了一些东西, 或者你合并了错误的分支, 亦或你强推了后找不到你自己的提交(commit)了。有些时候, 你一直都做得很好, 但你想回到以前的某个状态。 + +这就是 `git reflog` 的目的, `reflog` 记录对分支顶端(the tip of a branch)的任何改变, 即使那个顶端没有被任何分支或标签引用。基本上, 每次HEAD的改变, 一条新的记录就会增加到`reflog`。遗憾的是,这只对本地分支起作用,且它只跟踪动作 (例如,不会跟踪一个没有被记录的文件的任何改变)。 ```sh -git push -f +(master)$ git reflog +0a2e358 HEAD@{0}: reset: moving to HEAD~2 +0254ea7 HEAD@{1}: checkout: moving from 2.2 to master +c10f740 HEAD@{2}: checkout: moving from master to 2.2 ``` -### 回退到指定版本号 +上面的reflog展示了从master分支签出(checkout)到2.2 分支,然后再签回。 那里,还有一个硬重置(hard reset)到一个较旧的提交。最新的动作出现在最上面以 `HEAD@{0}`标识. -先找到想要回退版本的上一个版本 HASH,然后执行以下命令: +如果事实证明你不小心回移(move back)了提交(commit), reflog 会包含你不小心回移前master上指向的提交(0254ea7)。 +```sh +$ git reset --hard 0254ea7 ``` -git reset --soft 747abfa5689d9a14da1ed7a14a762fcf6368ef13 -git push -f -``` +然后使用git reset就可以把master改回到之前的commit,这提供了一个在历史被意外更改情况下的安全网。 -## 资料 +## 资源 -https://github.com/521xueweihan/git-tips +* https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-CN.md +* https://github.com/521xueweihan/git-tips \ No newline at end of file diff --git a/docs/git/appendix/git-resource.md b/docs/git/appendix/git-resource.md index c612a7d..e5cf4e0 100644 --- a/docs/git/appendix/git-resource.md +++ b/docs/git/appendix/git-resource.md @@ -4,13 +4,16 @@ * [Git 官网](https://git-scm.com/) * [Git Github](https://github.com/git/git) -* [Git 官方推荐教程](https://git-scm.com/book/zh/v2) -## Github 资源 +## 模板 -* [gitignore 模板](https://github.com/github/gitignore) -* [gitattributes 模板](https://github.com/alexkaratarakis/gitattributes) -* [github-cheat-sheet](https://github.com/tiimgreen/github-cheat-sheet) +* [gitignore 模板](https://github.com/github/gitignore) - .gitignore 文件模板 +* [gitattributes 模板](https://github.com/alexkaratarakis/gitattributes) - .gitattributes 文件模板 +* [github-cheat-sheet](https://github.com/tiimgreen/github-cheat-sheet) - git 命令简略图表 + +## Git 书 + +* [Git 官方推荐教程](https://git-scm.com/book/zh/v2) - Scott Chacon 的 Git 书。 ## Git 教程 @@ -18,15 +21,29 @@ * [廖雪峰的Git教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) * [有关 git 的学习资料](https://github.com/xirong/my-git) -## Git 工具 - -* [Git 官网展示的客户端工具列表](https://git-scm.com/downloads/guis) -* [git-extras](https://github.com/tj/git-extras) - Git 扩展工具。 -* [gogs](https://github.com/gogits/gogs) - 极易搭建的自助 Git 服务。 -* [gitflow](https://github.com/nvie/gitflow) - 应用 [fit-flow](http://nvie.com/posts/a-successful-git-branching-model/) 模型的工具。 - ## 文章 * [Git Cookbook](https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-CN.md) * [Git 奇技淫巧](https://github.com/521xueweihan/git-tips) * [Git 风格指南](https://github.com/aseaday/git-style-guide) + +## Git 工具 + +* [guis](https://git-scm.com/downloads/guis) - Git 官网展示的客户端工具列表。 +* [gogs](https://github.com/gogits/gogs) - 极易搭建的自助 Git 服务。 +* [gitflow](https://github.com/nvie/gitflow) - 应用 [fit-flow](http://nvie.com/posts/a-successful-git-branching-model/) 模型的工具。 +* [firstaidgit.io](http://firstaidgit.io/) 一个可搜索的最常被问到的Git的问题 +* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - 一堆有用的额外的Git脚本 +* [git-extras](https://github.com/tj/git-extras) - GIT 工具集 -- repo summary, repl, changelog population, author commit percentages and more +* [git-fire](https://github.com/qw3rtman/git-fire) - git-fire 是一个 Git 插件,用于帮助在紧急情况下添加所有当前文件, 做提交(committing), 和推(push)到一个新分支(阻止合并冲突)。 +* [git-tips](https://github.com/git-tips/tips) - Git小提示 +* [git-town](https://github.com/Originate/git-town) - 通用,高级Git工作流支持! http://www.git-town.com + +## GUI客户端(GUI Clients) + +* [GitKraken](https://www.gitkraken.com/) - 豪华的Git客户端 Windows, Mac & Linux +* [git-cola](https://git-cola.github.io/) - 另外一个Git客户端 Windows & OS X +* [GitUp](https://github.com/git-up/GitUp) - 一个新的Git客户端,在处理Git的复杂性上有自己的特点 +* [gitx-dev](https://rowanj.github.io/gitx/) - 图形化的Git客户端 OS X +* [Source Tree](https://www.sourcetreeapp.com/) - 免费的图形化Git客户端 Windows & OS X +* [Tower](http://www.git-tower.com/) - 图形化Git客户端 OS X(付费) \ No newline at end of file diff --git a/docs/git/basics/git-configuration.md b/docs/git/basics/git-configuration.md new file mode 100644 index 0000000..860c47e --- /dev/null +++ b/docs/git/basics/git-configuration.md @@ -0,0 +1,220 @@ +# Git 配置 + +## 配置 Git + +Git 使用一系列配置文件来保存你自定义的行为。 它首先会查找 `/etc/gitconfig` 文件,该文件含有系统里每位用户及他们所拥有的仓库的配置值。 如果你传递 `--system` 选项给 `git config`,它就会读写该文件。 + +接下来 Git 会查找每个用户的 `~/.gitconfig` 文件(或者 `~/.config/git/config` 文件)。 你可以传递 `--global` 选项让 Git 读写该文件。 + +最后 Git 会查找你正在操作的版本库所对应的 Git 目录下的配置文件(`.git/config`)。 这个文件中的值只对该版本库有效。 + +以上三个层次中每层的配置(系统、全局、本地)都会覆盖掉上一层次的配置,所以 `.git/config` 中的值会覆盖掉 `/etc/gitconfig` 中所对应的值。 + +## 客户端基本配置 + +### `core.editor` + +默认情况下,Git 会调用环境变量(`$VISUAL` 或 `$EDITOR`)设置的任意文本编辑器,如果没有设置,会调用 `vi` 来创建和编辑你的提交以及标签信息。 你可以使用 `core.editor` 选项来修改默认的编辑器: + +``` +$ git config --global core.editor emacs +``` + +现在,无论你定义了什么终端编辑器,Git 都会调用 Emacs 编辑信息。 + +### `commit.template` + +如果把此项指定为你的系统上某个文件的路径,当你提交的时候, Git 会使用该文件的内容作为提交的默认信息。 例如:假设你创建了一个叫 `~/.gitmessage.txt` 的模板文件,类似这样: + +``` +subject line + +what happened + +[ticket: X] +``` + +要想让 Git 把它作为运行 `git commit` 时显示在你的编辑器中的默认信息, 如下设置 `commit.template`: + +``` +$ git config --global commit.template ~/.gitmessage.txt +$ git commit +``` + +然后当你提交时,编辑器中就会显示如下的提交信息占位符: + +``` +subject line + +what happened + +[ticket: X] +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# On branch master +# Changes to be committed: +# (use "git reset HEAD ..." to unstage) +# +# modified: lib/test.rb +# +~ +~ +".git/COMMIT_EDITMSG" 14L, 297C +``` + +如果你的团队对提交信息有格式要求,可以在系统上创建一个文件,并配置 Git 把它作为默认的模板,这样可以更加容易地使提交信息遵循格式。 + +### `core.pager` + +该配置项指定 Git 运行诸如 `log` 和 `diff` 等命令所使用的分页器。 你可以把它设置成用 `more` 或者任何你喜欢的分页器(默认用的是 `less`),当然也可以设置成空字符串,关闭该选项: + +``` +$ git config --global core.pager '' +``` + +这样不管命令的输出量多少,Git 都会在一页显示所有内容。 + +### `user.signingkey` + +如果你要创建经签署的含附注的标签(正如 [签署工作](https://git-scm.com/book/zh/v2/ch00/r_signing) 所述),那么把你的 GPG 签署密钥设置为配置项会更好。 如下设置你的密钥 ID: + +``` +$ git config --global user.signingkey +``` + +现在,你每次运行 `git tag` 命令时,即可直接签署标签,而无需定义密钥: + +``` +$ git tag -s +``` + +### `core.excludesfile` + +正如 [忽略文件](https://git-scm.com/book/zh/v2/ch00/r_ignoring) 所述,你可以在你的项目的 `.gitignore` 文件里面规定无需纳入 Git 管理的文件的模板,这样它们既不会出现在未跟踪列表,也不会在你运行 `git add` 后被暂存。 + +不过有些时候,你想要在你所有的版本库中忽略掉某一类文件。 如果你的操作系统是 OS X,很可能就是指`.DS_Store`。 如果你把 Emacs 或 Vim 作为首选的编辑器,你肯定知道以 `~` 结尾的临时文件。 + +这个配置允许你设置类似于全局生效的 `.gitignore` 文件。 如果你按照下面的内容创建一个`~/.gitignore_global` 文件: + +``` +*~ +.DS_Store +``` + +……然后运行 `git config --global core.excludesfile ~/.gitignore_global`,Git 将把那些文件永远地拒之门外。 + +### `help.autocorrect` + +假如你打错了一条命令,会显示: + +``` +$ git chekcout master +git:'chekcout' 不是一个 git 命令。参见 'git --help'。 + +您指的是这个么? + checkout +``` + +Git 会尝试猜测你的意图,但是它不会越俎代庖。 如果你把 `help.autocorrect` 设置成 1,那么只要有一个命令被模糊匹配到了,Git 会自动运行该命令。 + +``` +$ git chekcout master +警告:您运行一个不存在的 Git 命令 'chekcout'。继续执行假定您要要运行的 +是 'checkout' +在 0.1 秒钟后自动运行... +``` + +注意提示信息中的“0.1 秒”。`help.autocorrect` 接受一个代表十分之一秒的整数。 所以如果你把它设置为 50, Git 将在自动执行命令前给你 5 秒的时间改变主意。 + +## 格式化与多余的空白字符 + +格式化与多余的空白字符是许多开发人员在协作时,特别是在跨平台情况下,不时会遇到的令人头疼的琐碎的问题。 由于编辑器的不同或者文件行尾的换行符在 Windows 下被替换了,一些细微的空格变化会不经意地混入提交的补丁或其它协作成果中。 不用怕,Git 提供了一些配置项来帮助你解决这些问题。 + +### `core.autocrlf` + +假如你正在 Windows 上写程序,而你的同伴用的是其他系统(或相反),你可能会遇到 CRLF 问题。 这是因为 Windows 使用回车(CR)和换行(LF)两个字符来结束一行,而 Mac 和 Linux 只使用换行(LF)一个字符。 虽然这是小问题,但它会极大地扰乱跨平台协作。许多 Windows 上的编辑器会悄悄把行尾的换行字符转换成回车和换行,或在用户按下 Enter 键时,插入回车和换行两个字符。 + +Git 可以在你提交时自动地把回车和换行转换成换行,而在检出代码时把换行转换成回车和换行。 你可以用 `core.autocrlf` 来打开此项功能。 如果是在 Windows 系统上,把它设置成 `true`,这样在检出代码时,换行会被转换成回车和换行: + +``` +$ git config --global core.autocrlf true +``` + +如果使用以换行作为行结束符的 Linux 或 Mac,你不需要 Git 在检出文件时进行自动的转换;然而当一个以回车加换行作为行结束符的文件不小心被引入时,你肯定想让 Git 修正。 你可以把 `core.autocrlf` 设置成 input 来告诉 Git 在提交时把回车和换行转换成换行,检出时不转换: + +``` +$ git config --global core.autocrlf input +``` + +这样在 Windows 上的检出文件中会保留回车和换行,而在 Mac 和 Linux 上,以及版本库中会保留换行。 + +如果你是 Windows 程序员,且正在开发仅运行在 Windows 上的项目,可以设置 `false` 取消此功能,把回车保留在版本库中: + +``` +$ git config --global core.autocrlf false +``` + +### `core.whitespace` + +Git 预先设置了一些选项来探测和修正多余空白字符问题。 它提供了六种处理多余空白字符的主要选项 —— 其中三个默认开启,另外三个默认关闭,不过你可以自由地设置它们。 + +默认被打开的三个选项是:`blank-at-eol`,查找行尾的空格;`blank-at-eof`,盯住文件底部的空行;`space-before-tab`,警惕行头 tab 前面的空格。 + +默认被关闭的三个选项是:`indent-with-non-tab`,揪出以空格而非 tab 开头的行(你可以用 `tabwidth`选项控制它);`tab-in-indent`,监视在行头表示缩进的 tab;`cr-at-eol`,告诉 Git 忽略行尾的回车。 + +通过设置 `core.whitespace`,你可以让 Git 按照你的意图来打开或关闭以逗号分割的选项。 要想关闭某个选项,你可以在输入设置选项时不指定它或在它前面加个 `-`。 例如,如果你想要打开除 `cr-at-eol` 之外的所有选项: + +``` +$ git config --global core.whitespace \ + trailing-space,space-before-tab,indent-with-non-tab +``` + +当你运行 `git diff` 命令并尝试给输出着色时,Git 将探测到这些问题,因此你在提交前就能修复它们。 用`git apply` 打补丁时你也会从中受益。 如果正准备应用的补丁存有特定的空白问题,你可以让 Git 在应用补丁时发出警告: + +``` +$ git apply --whitespace=warn +``` + +或者让 Git 在打上补丁前自动修正此问题: + +``` +$ git apply --whitespace=fix +``` + +这些选项也能运用于 `git rebase`。 如果提交了有空白问题的文件,但还没推送到上游,你可以运行 `git rebase --whitespace=fix` 来让 Git 在重写补丁时自动修正它们。 + +## 服务器端配置 + +Git 服务器端的配置项相对来说并不多,但仍有一些饶有生趣的选项值得你一看。 + +### `receive.fsckObjects` + +Git 能够确认每个对象的有效性以及 SHA-1 检验和是否保持一致。 但 Git 不会在每次推送时都这么做。这个操作很耗时间,很有可能会拖慢提交的过程,特别是当库或推送的文件很大的情况下。 如果想在每次推送时都要求 Git 检查一致性,设置 `receive.fsckObjects` 为 true 来强迫它这么做: + +``` +$ git config --system receive.fsckObjects true +``` + +现在 Git 会在每次推送生效前检查库的完整性,确保没有被有问题的客户端引入破坏性数据。 + +### `receive.denyNonFastForwards` + +如果你变基已经被推送的提交,继而再推送,又或者推送一个提交到远程分支,而这个远程分支当前指向的提交不在该提交的历史中,这样的推送会被拒绝。 这通常是个很好的策略,但有时在变基的过程中,你确信自己需要更新远程分支,可以在 push 命令后加 `-f` 标志来强制更新(force-update)。 + +要禁用这样的强制更新推送(force-pushes),可以设置 `receive.denyNonFastForwards`: + +``` +$ git config --system receive.denyNonFastForwards true +``` + +稍后我们会提到,用服务器端的接收钩子也能达到同样的目的。 那种方法可以做到更细致的控制,例如禁止某一类用户做非快进(non-fast-forwards)推送。 + +### `receive.denyDeletes` + +有一些方法可以绕过 `denyNonFastForwards` 策略。其中一种是先删除某个分支,再连同新的引用一起推送回该分支。 把 `receive.denyDeletes` 设置为 true 可以把这个漏洞补上: + +``` +$ git config --system receive.denyDeletes true +``` + +这样会禁止通过推送删除分支和标签 — 没有用户可以这么做。 要删除远程分支,必须从服务器手动删除引用文件。 通过用户访问控制列表(ACL)也能够在用户级的粒度上实现同样的功能,你将在 [使用强制策略的一个例子](https://git-scm.com/book/zh/v2/ch00/r_an_example_git_enforced_policy) 一节学到具体的做法。