1 | git
|
2 | ===
|
3 |
|
4 | 是目前世界上最先进的分布式版本控制系统
|
5 |
|
6 | ## 补充说明
|
7 |
|
8 | **git命令** 很多人都知道,Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。
|
9 |
|
10 | Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?
|
11 |
|
12 | 事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!
|
13 |
|
14 | 你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。
|
15 |
|
16 | 不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。
|
17 |
|
18 | 安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。
|
19 |
|
20 | Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:
|
21 |
|
22 | Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。
|
23 |
|
24 | Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。
|
25 |
|
26 | 历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。
|
27 |
|
28 | [Git常用命令清单](https://github.com/jaywcjlove/handbook/blob/master/other/Git%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E6%B8%85%E5%8D%95.md)
|
29 |
|
30 | ### 语法
|
31 |
|
32 | ```shell
|
33 | git [--version] [--help] [-C <path>] [-c name=value]
|
34 | [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
|
35 | [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
|
36 | [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
|
37 | <command> [<args>]
|
38 | ```
|
39 |
|
40 | ### 选项
|
41 |
|
42 | ```shell
|
43 | add 将文件内容添加到索引
|
44 | bisect 通过二进制查找引入错误的更改
|
45 | branch 列出,创建或删除分支
|
46 | checkout 检查分支或路径到工作树
|
47 | clone 将存储库克隆到新目录中
|
48 | commit 将更改记录到存储库
|
49 | diff 显示提交,提交和工作树等之间的更改
|
50 | fetch 从另一个存储库下载对象和引用
|
51 | grep 打印匹配图案的行
|
52 | init 创建一个空的Git仓库或重新初始化一个现有的
|
53 | log 显示提交日志
|
54 | merge 加入两个或更多的开发历史
|
55 | mv 移动或重命名文件,目录或符号链接
|
56 | pull 从另一个存储库或本地分支获取并合并
|
57 | push 更新远程引用以及相关对象
|
58 | rebase 转发端口本地提交到更新的上游头
|
59 | reset 将当前HEAD复位到指定状态
|
60 | rm 从工作树和索引中删除文件
|
61 | show 显示各种类型的对象
|
62 | status 显示工作树状态
|
63 | tag 创建,列出,删除或验证使用GPG签名的标签对象
|
64 | ```
|
65 |
|
66 | ### 例子
|
67 |
|
68 | init
|
69 |
|
70 | `git init` #初始化
|
71 |
|
72 | status
|
73 |
|
74 | `git status` #获取状态
|
75 |
|
76 | add
|
77 |
|
78 | `git add file` # .或*代表全部添加
|
79 | `git rm --cached <added_file_to_undo>` # 在commit之前撤销git add操作
|
80 | `git reset head` # 好像比上面`git rm --cached`更方便
|
81 |
|
82 | commit
|
83 |
|
84 | `git commit -m "message"` #此处注意乱码
|
85 |
|
86 | remote
|
87 |
|
88 | `git remote add origin git@github.com:JSLite/test.git` #添加源
|
89 |
|
90 | push
|
91 |
|
92 | ```shell
|
93 | git push -u origin master # push 同时设置默认跟踪分支
|
94 | git push origin master
|
95 | git push -f origin master # 强制推送文件,缩写 -f(全写--force)
|
96 | ```
|
97 |
|
98 | clone
|
99 |
|
100 | `git clone git://github.com/JSLite/JSLite.js.git `
|
101 | `git clone git://github.com/JSLite/JSLite.js.git mypro` #克隆到自定义文件夹
|
102 | `git clone [user@]example.com:path/to/repo.git/` #SSH协议还有另一种写法。
|
103 |
|
104 | git clone支持多种协议,除了HTTP(s)以外,还支持SSH、Git、本地文件协议等,下面是一些例子。`git clone <版本库的网址> <本地目录名>`
|
105 |
|
106 | ```shell
|
107 | $ git clone http[s]://example.com/path/to/repo.git/
|
108 | $ git clone ssh://example.com/path/to/repo.git/
|
109 | $ git clone git://example.com/path/to/repo.git/
|
110 | $ git clone /opt/git/project.git
|
111 | $ git clone file:///opt/git/project.git
|
112 | $ git clone ftp[s]://example.com/path/to/repo.git/
|
113 | $ git clone rsync://example.com/path/to/repo.git/
|
114 | ```
|
115 |
|
116 | ## 配置
|
117 |
|
118 | 首先是配置帐号信息 `ssh -T git@github.com` 测试。
|
119 |
|
120 | ## 修改项目中的个人信息
|
121 |
|
122 | ```shell
|
123 | git help config # 获取帮助信息,查看修改个人信息的参数
|
124 | git config --global user.name "小弟调调" # 修改全局名字
|
125 | git config --global user.email "wowohoo@qq.com" # 修改全局邮箱
|
126 | git config --list # 查看配置的信息
|
127 | ```
|
128 |
|
129 | ### 配置自动换行
|
130 |
|
131 | 自动转换坑太大,提交到git是自动将换行符转换为lf
|
132 |
|
133 | ```shell
|
134 | git config --global core.autocrlf input
|
135 | ```
|
136 |
|
137 | ## 常见使用场景
|
138 |
|
139 | ### 创建SSH密钥
|
140 |
|
141 | 这个密钥用来跟 github 通信,在本地终端里生成然后上传到 github
|
142 |
|
143 | ```shell
|
144 | ssh-keygen -t rsa -C 'wowohoo@qq.com' # 生成密钥
|
145 | ssh-keygen -t rsa -C "wowohoo@qq.com" -f ~/.ssh/ww_rsa # 指定生成目录文件名字
|
146 | ssh -T git@github.com # 测试是否成功
|
147 | ```
|
148 |
|
149 | ### 多账号ssh配置
|
150 |
|
151 | **1.生成指定名字的密钥**
|
152 |
|
153 | `ssh-keygen -t rsa -C "邮箱地址" -f ~/.ssh/jslite_rsa`
|
154 | 会生成 `jslite_rsa` 和 `jslite_rsa.pub` 这两个文件
|
155 |
|
156 | **2.密钥复制到托管平台上**
|
157 |
|
158 | `vim ~/.ssh/jslite_rsa.pub`
|
159 | 打开公钥文件 `jslite_rsa.pub` ,并把内容复制至代码托管平台上
|
160 |
|
161 | **3.修改config文件**
|
162 |
|
163 | `vim ~/.ssh/config` #修改config文件,如果没有创建 `config`
|
164 |
|
165 | ```shell
|
166 | Host jslite.github.com
|
167 | HostName github.com
|
168 | User git
|
169 | IdentityFile ~/.ssh/jslite_rsa
|
170 |
|
171 | Host work.github.com
|
172 | HostName github.com
|
173 | # Port 服务器open-ssh端口(默认:22,默认时一般不写此行)
|
174 | # PreferredAuthentications 配置登录时用什么权限认证
|
175 | # publickey|password publickey|keyboard-interactive等
|
176 | User git
|
177 | IdentityFile ~/.ssh/work_rsa
|
178 | ```
|
179 |
|
180 | - `Host` 这里是个别名可以随便命名
|
181 | - `HostName` 一般是网站如:`git@ss.github.com:username/repo.git` 填写 `github.com`
|
182 | - `User` 通常填写`git`
|
183 | - `IdentityFile` 使用的公钥文件地址
|
184 |
|
185 | **4.测试**
|
186 |
|
187 | ```shell
|
188 | ssh -T git@jslite.github.com # `@`后面跟上定义的Host
|
189 | ssh -T work.github.com # 通过别名测试
|
190 | ssh -i ~/公钥文件地址 Host别名 # 如 ssh -i ~/.ssh/work_rsa work.github.com
|
191 | ```
|
192 |
|
193 | **5.使用**
|
194 |
|
195 | ```shell
|
196 | # 原来的写法
|
197 | git clone git@github.com:<jslite的用户名>/learngit.git
|
198 | # 现在的写法
|
199 | git clone git@jslite.github.com:<jslite的用户名>/learngit.git
|
200 | git clone git@work.github.com:<work的用户名>/learngit.git
|
201 | ```
|
202 |
|
203 | **5.注意**
|
204 |
|
205 | 如果你修改了id_rsa的名字,你需要将ssh key添加到SSH agent中,如:
|
206 |
|
207 | ```shell
|
208 | ssh-add ~/.ssh/jslite_rsa
|
209 | ssh-add -l # 查看所有的key
|
210 | ssh-add -D # 删除所有的key
|
211 | ssh-add -d ~/.ssh/jslite_rsa # 删除指定的key
|
212 | ```
|
213 |
|
214 | ### 免密码登录远程服务器
|
215 |
|
216 | ```shell
|
217 | $ ssh-keygen -t rsa -P '' -f ~/.ssh/aliyunserver.key
|
218 | $ ssh-copy-id -i ~/.ssh/aliyunserver.key.pub root@192.168.182.112 # 这里需要输入密码一次
|
219 | ```
|
220 |
|
221 | 编辑 `~/.ssh/config`
|
222 |
|
223 | ```shell
|
224 | Host aliyun1
|
225 | HostName 192.168.182.112
|
226 | User root
|
227 | PreferredAuthentications publickey
|
228 | IdentityFile ~/.ssh/aliyunserver.key
|
229 | ```
|
230 |
|
231 | 上面配置完了,可以通过命令登录,不需要输入IP地址和密码 `ssh aliyun1`
|
232 |
|
233 | ### https协议下提交代码免密码
|
234 |
|
235 | ```shell
|
236 | git clone https://github.com/username/rep.git
|
237 | ```
|
238 |
|
239 | 通过上面方式克隆可能需要密码,解决办法:进入当前克隆的项目 `vi rep/.git/config` 编辑 `config`, 按照下面方式修改,你就可以提交代码不用输入密码了。
|
240 |
|
241 | ```shell
|
242 | [core]
|
243 | repositoryformatversion = 0
|
244 | filemode = true
|
245 | bare = false
|
246 | logallrefupdates = true
|
247 | ignorecase = true
|
248 | precomposeunicode = true
|
249 | [remote "origin"]
|
250 | - url = https://github.com/username/rep.git
|
251 | + url = https://用户名:密码@github.com/username/rep.git
|
252 | fetch = +refs/heads/*:refs/remotes/origin/*
|
253 | [branch "master"]
|
254 | remote = origin
|
255 | merge = refs/heads/master
|
256 | ```
|
257 |
|
258 | ### 文件推向3个git库
|
259 |
|
260 | **1. 增加3个远程库地址**
|
261 |
|
262 | ```shell
|
263 | git remote add origin https://github.com/JSLite/JSLite.git
|
264 | git remote set-url --add origin https://gitlab.com/wang/JSLite.js.git
|
265 | git remote set-url --add origin https://oschina.net/wang/JSLite.js.git
|
266 | ```
|
267 |
|
268 | **2. 删除其中一个 set-url 地址**
|
269 |
|
270 | ```shell
|
271 | usage: git remote set-url [--push] <name> <newurl> [<oldurl>]
|
272 | or: git remote set-url --add <name> <newurl>
|
273 | or: git remote set-url --delete <name> <url>
|
274 | ```
|
275 |
|
276 | `git remote set-url --delete origin https://oschina.net/wang/JSLite.js.git`
|
277 |
|
278 | **3.推送代码**
|
279 |
|
280 | ```shell
|
281 | git push origin master
|
282 | git push -f origin master # 强制推送
|
283 | ```
|
284 |
|
285 | **4.拉代码**
|
286 |
|
287 | 只能拉取 `origin` 里的一个url地址,这个fetch-url
|
288 | 默认为你添加的到 `origin`的第一个地址
|
289 |
|
290 | ```shell
|
291 | git pull origin master
|
292 | git pull --all # 获取远程所有内容包括tag
|
293 | git pull origin next:master # 取回origin主机的next分支,与本地的master分支合并
|
294 | git pull origin next # 远程分支是与当前分支合并
|
295 |
|
296 | # 上面一条命令等同于下面两条命令
|
297 | git fetch origin
|
298 | git merge origin/next
|
299 | ```
|
300 |
|
301 | 如果远程主机删除了某个分支,默认情况下,git pull 不会在拉取远程分支的时候,删除对应的本地分支。这是为了防止,由于其他人操作了远程主机,导致git pull不知不觉删除了本地分支。
|
302 | 但是,你可以改变这个行为,加上参数 -p 就会在本地删除远程已经删除的分支。
|
303 |
|
304 | ```shell
|
305 | $ git pull -p
|
306 | # 等同于下面的命令
|
307 | $ git fetch --prune origin
|
308 | $ git fetch -p
|
309 | ```
|
310 |
|
311 | **5.更改pull**
|
312 |
|
313 | 只需要更改config文件里,那三个url的顺序即可,fetch-url会直接对应排行第一的那个utl连接。
|
314 |
|
315 |
|
316 | ### 修改远程仓库地址
|
317 |
|
318 | ```shell
|
319 | git remote remove origin # 删除该远程路径
|
320 | git remote add origin git@jslite.github.com:JSLite/JSLite.git # 添加远程路径
|
321 | ```
|
322 |
|
323 | ### 撤销远程记录
|
324 |
|
325 | ```shell
|
326 | git reset --hard HEAD~1 # 撤销一条记录
|
327 | git push -f origin HEAD:master # 同步到远程仓库
|
328 | ```
|
329 |
|
330 | ### 放弃本地的文件修改
|
331 |
|
332 | ```shell
|
333 | git reset --hard FETCH_HEAD # FETCH_HEAD表示上一次成功git pull之后形成的commit点。然后git pull
|
334 | ```
|
335 |
|
336 | `git reset --hard FETCH_HEAD` 出现错误
|
337 |
|
338 | ```shell
|
339 | git pull
|
340 | You are not currently on a branch, so I cannot use any
|
341 | 'branch.<branchname>.merge' in your configuration file.
|
342 | Please specify which remote branch you want to use on the command
|
343 | line and try again (e.g. 'git pull <repository> <refspec>').
|
344 | See git-pull(1) FOR details.
|
345 | ```
|
346 |
|
347 | 解决方法:
|
348 |
|
349 | ```shell
|
350 | git checkout -b temp # 新建+切换到temp分支
|
351 | git checkout master
|
352 | ```
|
353 |
|
354 | ### 最简单放弃本地修改内容
|
355 |
|
356 | ```shell
|
357 | # 如果有的修改以及加入暂存区的话
|
358 | git reset --hard
|
359 | # 还原所有修改,不会删除新增的文件
|
360 | git checkout .
|
361 | # 下面命令会删除新增的文件
|
362 | git clean -xdf
|
363 | ```
|
364 |
|
365 | 通过存储暂存区stash,在删除暂存区的方法放弃本地修改。
|
366 |
|
367 | ```shell
|
368 | git stash && git stash drop
|
369 | ```
|
370 |
|
371 | ### 回滚到某个commit提交
|
372 |
|
373 | ```shell
|
374 | git revert HEAD~1 # 撤销一条记录 会弹出 commit 编辑
|
375 | git push # 提交回滚
|
376 | ```
|
377 |
|
378 |
|
379 | ### 回退到某一个版本
|
380 |
|
381 | ```shell
|
382 | git reset --hard <hash>
|
383 | # 例如 git reset --hard a3hd73r
|
384 | # --hard代表丢弃工作区的修改,让工作区与版本代码一模一样,与之对应,
|
385 | # --soft参数代表保留工作区的修改。
|
386 | ```
|
387 |
|
388 | ### 去掉某个commit
|
389 |
|
390 | ```shell
|
391 | # 实质是新建了一个与原来完全相反的commit,抵消了原来commit的效果
|
392 | git revert <commit-hash>
|
393 | ```
|
394 |
|
395 | ### 新建一个空分支
|
396 |
|
397 | ```shell
|
398 | # 这种方式新建的分支(gh-pages)是没有 commit 记录的
|
399 | git checkout --orphan gh-pages
|
400 | # 删除新建的gh-pages分支原本的内容,如果不删除,提交将作为当前分支的第一个commit
|
401 | git rm -rf .
|
402 | # 查看一下状态 有可能上面一条命令,没有删除还没有提交的的文件
|
403 | git state
|
404 | ```
|
405 |
|
406 | ### 合并多个commit
|
407 |
|
408 | ```shell
|
409 | # 这个命令,将最近4个commit合并为1个,HEAD代表当前版本。
|
410 | # 将进入VIM界面,你可以修改提交信息。
|
411 | git rebase -i HEAD~4
|
412 | # 可以看到其中分为两个部分,上方未注释的部分是填写要执行的指令,
|
413 | # 而下方注释的部分则是指令的提示说明。指令部分中由前方的命令名称、commit hash 和 commit message 组成
|
414 | # 当前我们只要知道 pick 和 squash 这两个命令即可。
|
415 | # --> pick 的意思是要会执行这个 commit
|
416 | # --> squash 的意思是这个 commit 会被合并到前一个commit
|
417 |
|
418 | # 我们将 需要保留的 这个 commit 前方的命令改成 squash 或 s,然后输入:wq以保存并退出
|
419 | # 这是我们会看到 commit message 的编辑界面
|
420 |
|
421 | # 其中, 非注释部分就是两次的 commit message, 你要做的就是将这两个修改成新的 commit message。
|
422 | #
|
423 | # 输入wq保存并推出, 再次输入git log查看 commit 历史信息,你会发现这两个 commit 已经合并了。
|
424 | # 将修改强制推送到前端
|
425 | git push -f origin master
|
426 | ```
|
427 |
|
428 | ### 修改远程Commit记录
|
429 |
|
430 | ```shell
|
431 | git commit --amend
|
432 | # amend只能修改没有提交到线上的,最后一次commit记录
|
433 | git rebase -i HEAD~3
|
434 | # 表示要修改当前版本的倒数第三次状态
|
435 | # 将要更改的记录行首单词 pick 改为 edit
|
436 | pick 96dc3f9 doc: Update quick-start.md
|
437 | pick f1cce8a test(Transition):Add transition test (#47)
|
438 | pick 6293516 feat(Divider): Add Divider component.
|
439 | # Rebase eeb03a4..6293516 onto eeb03a4 (3 commands)
|
440 | #
|
441 | # Commands:
|
442 | # p, pick = use commit
|
443 | # r, reword = use commit, but edit the commit message
|
444 | # e, edit = use commit, but stop for amending
|
445 | # s, squash = use commit, but meld into previous commit
|
446 | # f, fixup = like "squash", but discard this commit's log message
|
447 | # x, exec = run command (the rest of the line) using shell
|
448 | # d, drop = remove commit
|
449 | ```
|
450 |
|
451 | 保存并退出,会弹出下面提示
|
452 |
|
453 | ```shell
|
454 | # You can amend the commit now, with
|
455 | #
|
456 | # git commit --amend
|
457 | #
|
458 | # Once you are satisfied with your changes, run
|
459 | #
|
460 | # git rebase --continue
|
461 |
|
462 | # 通过这条命令进入编辑页面更改commit,保存退出
|
463 | git commit --amend
|
464 | # 保存退出确认修改,继续执行 rebase,
|
465 | git rebase --continue
|
466 | # 如果修改多条记录反复执行上面两条命令直到完成所有修改
|
467 |
|
468 | # 最后,确保别人没有提交进行push,最好不要加 -f 强制推送
|
469 | git push -f origin master
|
470 | ```
|
471 |
|
472 |
|
473 |
|
474 | ### 添加忽略文件
|
475 |
|
476 | ```shell
|
477 | echo node_modules/ >> .gitignore
|
478 | ```
|
479 |
|
480 | ### 利用commit关闭一个issue
|
481 |
|
482 | 这个功能在Github上可以玩儿,Gitlab上特别老的版本不能玩儿哦,那么如何跟随着commit关闭一个issue呢? 在confirm merge的时候可以使用一下命令来关闭相关issue:
|
483 |
|
484 | `fixes #xxx`、 `fixed #xxx`、 `fix #xxx`、 `closes #xxx`、 `close #xxx`、 `closed #xxx`、
|
485 |
|
486 | ### 同步fork的上游仓库
|
487 |
|
488 | [Github教程同步fork教程](https://help.github.com/articles/syncing-a-fork/),[在Github上同步一个分支(fork)](http://www.miss77.net/549.html)
|
489 |
|
490 | **设置添加多个远程仓库地址。**
|
491 |
|
492 | 在同步之前,需要创建一个远程点指向上游仓库(repo).如果你已经派生了一个原始仓库,可以按照如下方法做。
|
493 |
|
494 | ```shell
|
495 | $ git remote -v
|
496 | # List the current remotes (列出当前远程仓库)
|
497 | # origin https://github.com/user/repo.git (fetch)
|
498 | # origin https://github.com/user/repo.git (push)
|
499 | $ git remote add upstream https://github.com/otheruser/repo.git
|
500 | # Set a new remote (设置一个新的远程仓库)
|
501 | $ git remote -v
|
502 | # Verify new remote (验证新的原唱仓库)
|
503 | # origin https://github.com/user/repo.git (fetch)
|
504 | # origin https://github.com/user/repo.git (push)
|
505 | # upstream https://github.com/otheruser/repo.git (fetch)
|
506 | # upstream https://github.com/otheruser/repo.git (push)
|
507 | ```
|
508 |
|
509 | **同步更新仓库内容**
|
510 |
|
511 | 同步上游仓库到你的仓库需要执行两步:首先你需要从远程拉去,之后你需要合并你希望的分支到你的本地副本分支。从上游的存储库中提取分支以及各自的提交内容。 `master` 将被存储在本地分支机构 `upstream/master`
|
512 |
|
513 | ```shell
|
514 | git fetch upstream
|
515 | # remote: Counting objects: 75, done.
|
516 | # remote: Compressing objects: 100% (53/53), done.
|
517 | # remote: Total 62 (delta 27), reused 44 (delta 9)
|
518 | # Unpacking objects: 100% (62/62), done.
|
519 | # From https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY
|
520 | # * [new branch] master -> upstream/master
|
521 | ```
|
522 |
|
523 | 检查你的 fork's 本地 `master` 分支
|
524 |
|
525 | ```shell
|
526 | git checkout master
|
527 | # Switched to branch 'master'
|
528 | ```
|
529 |
|
530 | 合并来自 `upstream/master` 的更改到本地 master 分支上。 这使你的前 fork's `master` 分支与上游资源库同步,而不会丢失你本地修改。
|
531 |
|
532 | ```shell
|
533 | git merge upstream/master
|
534 | # Updating a422352..5fdff0f
|
535 | # Fast-forward
|
536 | # README | 9 -------
|
537 | # README.md | 7 ++++++
|
538 | # 2 files changed, 7 insertions(+), 9 deletions(-)
|
539 | # delete mode 100644 README
|
540 | # create mode 100644 README.md
|
541 | ```
|
542 |
|
543 |
|
544 | ### 批量修改历史commit中的名字和邮箱
|
545 |
|
546 | **1.克隆仓库**
|
547 |
|
548 | 注意参数,这个不是普通的clone,clone下来的仓库并不能参与开发
|
549 |
|
550 | ```shell
|
551 | git clone --bare https://github.com/user/repo.git
|
552 | cd repo.git
|
553 | ```
|
554 |
|
555 | **2.命令行中运行代码**
|
556 |
|
557 | OLD_EMAIL原来的邮箱
|
558 | CORRECT_NAME更正的名字
|
559 | CORRECT_EMAIL更正的邮箱
|
560 |
|
561 | 将下面代码复制放到命令行中执行
|
562 |
|
563 | ```shell
|
564 | git filter-branch -f --env-filter '
|
565 | OLD_EMAIL="wowohoo@qq.com"
|
566 | CORRECT_NAME="小弟调调"
|
567 | CORRECT_EMAIL="更正的邮箱@qq.com"
|
568 | if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
|
569 | then
|
570 | export GIT_COMMITTER_NAME="$CORRECT_NAME"
|
571 | export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
|
572 | fi
|
573 | if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
|
574 | then
|
575 | export GIT_AUTHOR_NAME="$CORRECT_NAME"
|
576 | export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
|
577 | fi
|
578 | ' --tag-name-filter cat -- --branches --tags
|
579 | ```
|
580 |
|
581 | 执行过程
|
582 |
|
583 | ```shell
|
584 | Rewrite 160d4df2689ff6df3820563bfd13b5f1fb9ba832 (479/508) (16 seconds passed, remaining 0 predicted)
|
585 | Ref 'refs/heads/dev' was rewritten
|
586 | Ref 'refs/heads/master' was rewritten
|
587 | ```
|
588 |
|
589 | **3.同步到远程仓库**
|
590 |
|
591 | 同步到push远程git仓库
|
592 |
|
593 | ```shell
|
594 | git push --force --tags origin 'refs/heads/*'
|
595 | ```
|
596 |
|
597 | 我还遇到了如下面错误,lab默认给master分支加了保护,不允许强制覆盖。`Project(项目)`->`Setting`->`Repository` 菜单下面的`Protected branches`把master的保护去掉就可以了。修改完之后,建议把master的保护再加回来,毕竟强推不是件好事。
|
598 |
|
599 | ```shell
|
600 | remote: GitLab: You are not allowed to force push code to a protected branch on this project.
|
601 | ```
|
602 |
|
603 | 当上面的push 不上去的时候,先 `git pull` 确保最新代码
|
604 |
|
605 | ```shell
|
606 | git pull --allow-unrelated-histories
|
607 | # 或者指定分枝
|
608 | git pull origin master --allow-unrelated-histories
|
609 | ```
|
610 |
|
611 |
|
612 | ### 查看某个文件历史
|
613 |
|
614 | ```shell
|
615 | git log --pretty=oneline 文件名 # 列出文件的所有改动历史
|
616 | git show c178bf49 # 某次的改动的修改记录
|
617 | git log -p c178bf49 # 某次的改动的修改记录
|
618 | git blame 文件名 # 显示文件的每一行是在那个版本最后修改。
|
619 | git whatchanged 文件名 # 显示某个文件的每个版本提交信息:提交日期,提交人员,版本号,提交备注(没有修改细节)
|
620 | ```
|
621 |
|
622 | ### 打造自己的git命令
|
623 |
|
624 | ```shell
|
625 | git config --global alias.st status
|
626 | git config --global alias.br branch
|
627 | git config --global alias.co checkout
|
628 | git config --global alias.ci commit
|
629 | ```
|
630 |
|
631 | 配置好后再输入git命令的时候就不用再输入一大段了,例如我们要查看状态,只需:
|
632 |
|
633 | ```shell
|
634 | git st
|
635 | ```
|
636 |
|
637 | ### 中文乱码的解决方案
|
638 |
|
639 | ```shell
|
640 | git config --global core.quotepath false
|
641 | ```
|
642 |
|
643 | ## 新建仓库
|
644 |
|
645 | ### init
|
646 |
|
647 | `git init` #初始化
|
648 |
|
649 | ### status
|
650 |
|
651 | `git status` #获取状态
|
652 |
|
653 | ### add
|
654 |
|
655 | `git add file` # .或*代表全部添加
|
656 | `git rm --cached <added_file_to_undo>` # 在commit之前撤销git add操作
|
657 | `git reset head` # 好像比上面`git rm --cached`更方便
|
658 |
|
659 | ### commit
|
660 |
|
661 | `git commit -m "message"` #此处注意乱码
|
662 |
|
663 | ### remote
|
664 |
|
665 | `git remote add origin git@github.com:JSLite/test.git` #添加源
|
666 |
|
667 | ### push
|
668 |
|
669 | ```shell
|
670 | git push -u origin master # push同事设置默认跟踪分支
|
671 | git push origin master
|
672 | git push -f origin master # 强制推送文件,缩写 -f(全写--force)
|
673 | ```
|
674 |
|
675 | ## clone
|
676 |
|
677 | `git clone git://github.com/JSLite/JSLite.js.git `
|
678 | `git clone git://github.com/JSLite/JSLite.js.git mypro` #克隆到自定义文件夹
|
679 | `git clone [user@]example.com:path/to/repo.git/` #SSH协议还有另一种写法。
|
680 |
|
681 | git clone支持多种协议,除了HTTP(s)以外,还支持SSH、Git、本地文件协议等,下面是一些例子。`git clone <版本库的网址> <本地目录名>`
|
682 |
|
683 | ```shell
|
684 | $ git clone http[s]://example.com/path/to/repo.git/
|
685 | $ git clone ssh://example.com/path/to/repo.git/
|
686 | $ git clone git://example.com/path/to/repo.git/
|
687 | $ git clone /opt/git/project.git
|
688 | $ git clone file:///opt/git/project.git
|
689 | $ git clone ftp[s]://example.com/path/to/repo.git/
|
690 | $ git clone rsync://example.com/path/to/repo.git/
|
691 | ```
|
692 |
|
693 | ## 本地
|
694 |
|
695 | ### help
|
696 |
|
697 | ```shell
|
698 | git help config # 获取帮助信息
|
699 | ```
|
700 |
|
701 | ### add
|
702 |
|
703 | ```shell
|
704 | git add * # 跟踪新文件
|
705 | git add -u [path] # 添加[指定路径下]已跟踪文件
|
706 | ```
|
707 |
|
708 | ### rm
|
709 |
|
710 | ```shell
|
711 | rm *&git rm * # 移除文件
|
712 | git rm -f * # 移除文件
|
713 | git rm --cached * # 取消跟踪
|
714 | git mv file_from file_to # 重命名跟踪文件
|
715 | git log # 查看提交记录
|
716 | ```
|
717 |
|
718 | ### commit
|
719 |
|
720 | ```shell
|
721 | git commit #提交更新
|
722 | git commit -m 'message' #提交说明
|
723 | git commit -a #跳过使用暂存区域,把所有已经跟踪过的文件暂存起来一并提交
|
724 | git commit --amend #修改最后一次提交
|
725 | git commit log #查看所有提交,包括没有push的commit
|
726 | git commit -m "#133" #关联issue 任意位置带上# 符号加上issue号码
|
727 | git commit -m "fix #133" commit关闭issue
|
728 | git commit -m '概要描述'$'\n\n''1.详细描述'$'\n''2.详细描述' #提交简要描述和详细描述
|
729 | ```
|
730 |
|
731 | ### reset
|
732 |
|
733 | ```shell
|
734 | git reset HEAD * # 取消已经暂存的文件
|
735 | git reset --mixed HEAD * # 同上
|
736 | git reset --soft HEAD * # 重置到指定状态,不会修改索引区和工作树
|
737 | git reset --hard HEAD * # 重置到指定状态,会修改索引区和工作树
|
738 | git reset -- files * # 重置index区文件
|
739 | ```
|
740 |
|
741 | ### revert
|
742 |
|
743 | ```shell
|
744 | git revert HEAD # 撤销前一次操作
|
745 | git revert HEAD~ # 撤销前前一次操作
|
746 | git revert commit # 撤销指定操作
|
747 | ```
|
748 |
|
749 | ### checkout
|
750 |
|
751 | ```shell
|
752 | git checkout -- file # 取消对文件的修改(从暂存区——覆盖worktree file)
|
753 | git checkout branch|tag|commit -- file_name # 从仓库取出file覆盖当前分支
|
754 | git checkout HEAD~1 [文件] # 将会更新 working directory 去匹配某次 commit
|
755 | git checkout -- . # 从暂存区取出文件覆盖工作区
|
756 | git checkout -b gh-pages 0c304c9 # 这个表示 从当前分支 commit 哈希值为 0c304c9 的节点,分一个新的分支gh-pages出来,并切换到 gh-pages
|
757 | ```
|
758 |
|
759 | ### diff
|
760 |
|
761 | ```shell
|
762 | git diff file # 查看指定文件的差异
|
763 | git diff --stat # 查看简单的diff结果
|
764 | git diff # 比较Worktree和Index之间的差异
|
765 | git diff --cached # 比较Index和HEAD之间的差异
|
766 | git diff HEAD # 比较Worktree和HEAD之间的差异
|
767 | git diff branch # 比较Worktree和branch之间的差异
|
768 | git diff branch1 branch2 # 比较两次分支之间的差异
|
769 | git diff commit commit # 比较两次提交之间的差异
|
770 | git diff master..test # 上面这条命令只显示两个分支间的差异
|
771 | git diff master...test # 你想找出‘master’,‘test’的共有 父分支和'test'分支之间的差异,你用3个‘.'来取代前面的两个'.'
|
772 | ```
|
773 |
|
774 | ### stash
|
775 |
|
776 | ```shell
|
777 | git stash # 将工作区现场(已跟踪文件)储藏起来,等以后恢复后继续工作。
|
778 | git stash list # 查看保存的工作现场
|
779 | git stash apply # 恢复工作现场
|
780 | git stash drop # 删除stash内容
|
781 | git stash pop # 恢复的同时直接删除stash内容
|
782 | git stash apply stash@{0} # 恢复指定的工作现场,当你保存了不只一份工作现场时。
|
783 | ```
|
784 |
|
785 | ### merge
|
786 |
|
787 | ```shell
|
788 | git merge --squash test # 合并压缩,将test上的commit压缩为一条
|
789 | ```
|
790 |
|
791 | ### cherry-pick
|
792 |
|
793 | ```shell
|
794 | git cherry-pick commit # 拣选合并,将commit合并到当前分支
|
795 | git cherry-pick -n commit # 拣选多个提交,合并完后可以继续拣选下一个提交
|
796 | ```
|
797 |
|
798 | ### rebase
|
799 |
|
800 | ```shell
|
801 | git rebase master # 将master分之上超前的提交,变基到当前分支
|
802 | git rebase --onto master 169a6 # 限制回滚范围,rebase当前分支从169a6以后的提交
|
803 | git rebase --interactive # 交互模式,修改commit
|
804 | git rebase --continue # 处理完冲突继续合并
|
805 | git rebase --skip # 跳过
|
806 | git rebase --abort # 取消合并
|
807 | ```
|
808 |
|
809 | ## 分支branch
|
810 |
|
811 | ### 删除
|
812 |
|
813 | ```shell
|
814 | git push origin :branchName # 删除远程分支
|
815 | git push origin --delete new # 删除远程分支new
|
816 | git branch -d branchName # 删除本地分支,强制删除用-D
|
817 | git branch -d test # 删除本地test分支
|
818 | git branch -D test # 强制删除本地test分支
|
819 | git remote prune origin # 远程删除了,本地还能看到远程存在,这条命令删除远程不存在的分支
|
820 | ```
|
821 |
|
822 | ### 提交
|
823 |
|
824 | ```shell
|
825 | git push -u origin branchName # 提交分支到远程origin主机中
|
826 | ```
|
827 |
|
828 | ### 拉取
|
829 |
|
830 | `git fetch -p` #拉取远程分支时,自动清理 远程分支已删除,本地还存在的对应同名分支。
|
831 |
|
832 | ### 分支合并
|
833 |
|
834 | ```shell
|
835 | git merge branchName # 合并分支 - 将分支branchName和当前所在分支合并
|
836 | git merge origin/master # 在本地分支上合并远程分支。
|
837 | git rebase origin/master # 在本地分支上合并远程分支。
|
838 | git merge test # 将test分支合并到当前分支
|
839 | ```
|
840 |
|
841 | ### 重命名
|
842 |
|
843 | `git branch -m old new` #重命名分支
|
844 |
|
845 | ### 查看
|
846 |
|
847 | ```shell
|
848 | git branch # 列出本地分支
|
849 | git branch -r # 列出远端分支
|
850 | git branch -a # 列出所有分支
|
851 | git branch -v # 查看各个分支最后一个提交对象的信息
|
852 | git branch --merge # 查看已经合并到当前分支的分支
|
853 | git branch --no-merge # 查看为合并到当前分支的分支
|
854 | git remote show origin # 可以查看remote地址,远程分支
|
855 | ```
|
856 |
|
857 | ### 新建
|
858 |
|
859 | ```shell
|
860 | git branch test # 新建test分支
|
861 | git branch newBrach 3defc69 # 指定哈希3defc69,新建分支名字为newBrach
|
862 | git checkout -b newBrach origin/master # 取回远程主机的更新以后,在它的基础上创建一个新的分支
|
863 | git checkout -b newBrach 3defc69 # 以哈希值3defc69,新建 newBrach 分支,并切换到该分支
|
864 | ```
|
865 |
|
866 | ### 连接
|
867 |
|
868 | ```shell
|
869 | git branch --set-upstream dev origin/dev # 将本地dev分支与远程dev分支之间建立链接
|
870 | git branch --set-upstream master origin/next # 手动建立追踪关系
|
871 | ```
|
872 |
|
873 | ### 分支切换
|
874 |
|
875 | ```shell
|
876 | git checkout test # 切换到test分支
|
877 | git checkout -b test # 新建+切换到test分支
|
878 | git checkout -b test dev # 基于dev新建test分支,并切换
|
879 | ```
|
880 |
|
881 | ## 远端
|
882 |
|
883 | ```shell
|
884 | git fetch <远程主机名> <分支名> # fetch取回所有分支(branch)的更新
|
885 | git fetch origin remotebranch[:localbranch] # 从远端拉去分支[到本地指定分支]
|
886 | git merge origin/branch # 合并远端上指定分支
|
887 | git pull origin remotebranch:localbranch # 拉去远端分支到本地分支
|
888 | git push origin branch # 将当前分支,推送到远端上指定分支
|
889 | git push origin localbranch:remotebranch # 推送本地指定分支,到远端上指定分支
|
890 | git push origin :remotebranch # 删除远端指定分支
|
891 | git checkout -b [--track] test origin/dev # 基于远端dev分支,新建本地test分支[同时设置跟踪]
|
892 | ```
|
893 |
|
894 | ## submodule
|
895 |
|
896 | 克隆项目同时克隆submodule
|
897 |
|
898 | ```shell
|
899 | git clone https://github.com/jaywcjlove/handbook.git --depth=1 --recurse-submodules
|
900 | ```
|
901 |
|
902 | 克隆项目,之后再手动克隆 submodule 子项目
|
903 |
|
904 | ```shell
|
905 | git submodule add --force '仓库地址' '路径'
|
906 | # 其中,仓库地址是指子模块仓库地址,路径指将子模块放置在当前工程下的路径。
|
907 | # 注意:路径不能以 / 结尾(会造成修改不生效)、不能是现有工程已有的目录(不能順利 Clone)
|
908 | git submodule init # 初始化submodule
|
909 | git submodule update # 更新submodule(必须在根目录执行命令)
|
910 | git submodule update --init --recursive # 下载的工程带有submodule
|
911 | ```
|
912 |
|
913 | 当使用`git clone`下来的工程中带有submodule时,初始的时候,submodule的内容并不会自动下载下来的,此时,只需执行如下命令:
|
914 |
|
915 | ```shell
|
916 | git submodule foreach git pull # submodule 里有其他的 submodule 一次更新
|
917 | git submodule foreach git pull origin master # submodule更新
|
918 |
|
919 | git submodule foreach --recursive git submodule init
|
920 | git submodule foreach --recursive git submodule update
|
921 | ```
|
922 |
|
923 | ## 删除文件
|
924 |
|
925 | ```shell
|
926 | git rm -rf node_modules/
|
927 | ```
|
928 |
|
929 | ## remote
|
930 |
|
931 | git是一个分布式代码管理工具,所以可以支持多个仓库,在git里,服务器上的仓库在本地称之为remote。个人开发时,多源用的可能不多,但多源其实非常有用。
|
932 |
|
933 | ```shell
|
934 | git remote add origin1 git@github.com:yanhaijing/data.js.git
|
935 | git remote # 显示全部源
|
936 | git remote -v # 显示全部源+详细信息
|
937 | git remote rename origin1 origin2 # 重命名
|
938 | git remote rm origin # 删除
|
939 | git remote show origin # 查看指定源的全部信息
|
940 | ```
|
941 |
|
942 | ## 标签tag
|
943 |
|
944 | 当开发到一定阶段时,给程序打标签是非常棒的功能。
|
945 |
|
946 | ```shell
|
947 | git tag -a v0.1 -m 'my version 1.4' # 新建带注释标签
|
948 | git push origin --tags # 一次性推送所有分支
|
949 | git push origin v1.5 # 推送单个tag到orgin源上
|
950 | git tag -v v1.4.2.1 # 验证标签,验证已经签署的标签
|
951 | git show v1.5 # 看到对应的 GPG 签
|
952 |
|
953 | git tag # 列出现有标签
|
954 | git tag v0gi.1 # 新建标签
|
955 | git checkout tagname # 切换到标签
|
956 | git tag -d v0.1 # 删除标签
|
957 | git push origin :refs/tags/v0.1 # 删除远程标签
|
958 | git pull --all # 获取远程所有内容包括tag
|
959 | git --git-dir='<绝对地址>/.git' describe --tags HEAD # 查看本地版本信息
|
960 | ```
|
961 |
|
962 | ## 日志log
|
963 |
|
964 | ```shell
|
965 | git config format.pretty oneline #显示历史记录时,每个提交的信息只显示一行
|
966 | git config color.ui true #彩色的 git 输出
|
967 | git log #查看提交日志,从最近的提交开始显示
|
968 | git log --reverse #查看提交日志,从最远的提交开始显示
|
969 | git log --pretty=oneline #单行显示提交日志
|
970 | git log --graph --pretty=oneline --abbrev-commit
|
971 | git log -num #显示第几条log(倒数)
|
972 | git reflog #查看所有分支的所有操作记录
|
973 | git log --since=1.day #一天内的提交;你可以给出各种时间格式,比如说具体的某一天(“2008-01-15”),或者是多久以前(“2 years 1 day 3 minutes ago”)。
|
974 | git log --pretty="%h - %s" --author=自己的名字 #查看自己的日志
|
975 | git log -p -2 #展开两次更新显示每次提交的内容差异
|
976 | git log --stat #要快速浏览其他协作者提交的更新都作了哪些改动
|
977 | git log --pretty=format:"%h - %an, %ar : %s"#定制要显示的记录格式
|
978 | git log --pretty=format:'%h : %s' --date-order --graph # 拓扑顺序展示
|
979 | git log --pretty=format:'%h : %s - %ad' --date=short #日期YYYY-MM-DD显示
|
980 | git log <last tag> HEAD --pretty=format:%s # 只显示commit
|
981 | git config --global format.pretty '%h : %s - %ad' --date=short #日期YYYY-MM-DD显示 写入全局配置
|
982 | ```
|
983 |
|
984 | |选项 | 说明|选项 | 说明|
|
985 | |----|----|----|----|
|
986 | |%H |提交对象(commit)的完整哈希字串 |%ad |作者修订日期(可以用 -date= 选项定制格式)|
|
987 | |%h |提交对象的简短哈希字串 |%ar |作者修订日期,按多久以前的方式显示|
|
988 | |%T |树对象(tree)的完整哈希字串 |%cn |提交者(committer)的名字|
|
989 | |%t |树对象的简短哈希字串 |%ce |提交者的电子邮件地址|
|
990 | |%P |父对象(parent)的完整哈希字串 |%cd |提交日期|
|
991 | |%p |父对象的简短哈希字串 |%cr |提交日期,按多久以前的方式显示|
|
992 | |%an |作者(author)的名字 |%s |提交说明|
|
993 | |%ae |作者的电子邮件地址| - | - |
|
994 |
|
995 | [Pretty Formats](https://git-scm.com/docs/git-log#_pretty_formats)
|
996 |
|
997 | ## 重写历史
|
998 |
|
999 | ```shell
|
1000 | git commit --amend # 改变最近一次提交
|
1001 | git rebase -i HEAD~3 # 修改最近三次的提交说明,或者其中任意一次
|
1002 | git commit --amend # 保存好了,这些指示很明确地告诉了你该干什么
|
1003 | git rebase --continue # 修改提交说明,退出编辑器。
|
1004 | ```
|
1005 |
|
1006 | ```shell
|
1007 | pick f7f3f6d changed my name a bit
|
1008 | pick 310154e updated README formatting and added blame
|
1009 | pick a5f4a0d added cat-file
|
1010 | ```
|
1011 |
|
1012 | 改成
|
1013 |
|
1014 | ```
|
1015 | pick 310154e updated README formatting and added blame
|
1016 | pick f7f3f6d changed my name a bit
|
1017 | ```
|
1018 |
|
1019 | ### 删除仓库
|
1020 |
|
1021 | ```
|
1022 | cd ..
|
1023 | rm -rf repo.git
|
1024 | ```
|
1025 |
|
1026 | [Github官方教程](https://help.github.com/articles/changing-author-info/)
|
1027 |
|
1028 | ## 其它
|
1029 |
|
1030 | ```shell
|
1031 | git help * # 获取命令的帮助信息
|
1032 | git status # 获取当前的状态,非常有用,因为git会提示接下来的能做的操作
|
1033 | ```
|
1034 |
|
1035 |
|
1036 | ## 报错问题解决
|
1037 |
|
1038 | **1. `git fatal: protocol error: bad line length character: No s`**
|
1039 |
|
1040 | 解决办法:更换remote地址为 `http/https` 的
|
1041 |
|
1042 | **2. `The requested URL returned error: 403 Forbidden while accessing`**
|
1043 |
|
1044 | 解决github push错误的办法:
|
1045 |
|
1046 | ```shell
|
1047 | #vim 编辑器打开 当前项目中的config文件
|
1048 | vim .git/config
|
1049 |
|
1050 | #修改
|
1051 | [remote "origin"]
|
1052 | url = https://github.com/jaywcjlove/example.git
|
1053 |
|
1054 | #为下面代码
|
1055 | [remote "origin"]
|
1056 | url = https://jaywcjlove@github.com/jaywcjlove/example.git
|
1057 | ```
|
1058 |
|
1059 | **3. git status 显示中文问题**
|
1060 |
|
1061 | 在查看状态的时候 git status 如果是中文就显示下面的情况
|
1062 |
|
1063 | ```shell
|
1064 | \344\272\247\345\223\201\351\234\200\346\261\202
|
1065 | ```
|
1066 |
|
1067 | 解决这个问题方法是:
|
1068 |
|
1069 | ```shell
|
1070 | git config --global core.quotepath false
|
1071 | ```
|
1072 |
|
1073 | ## 参考资料
|
1074 |
|
1075 | - [Git官网](http://git-scm.com/)
|
1076 | - [**Github 15分钟学习Git**](https://try.github.io)
|
1077 | - [Git参考手册](http://gitref.org/zh/index.html)
|
1078 | - [Git简明手册](http://www.mceiba.com/tool/git-cheat-sheet.html)
|
1079 | - [Git Magic](http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/)
|
1080 | - [Git Community Book 中文版](http://gitbook.liuhui998.com/index.html)
|
1081 | - [Pro Git](http://git-scm.com/book/en/v2)
|
1082 | - [图解Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)
|
1083 | - [git-简明指南](http://rogerdudler.github.io/git-guide/index.zh.html)
|
1084 | - [learnGitBranching 在线学习工具](http://pcottle.github.io/learnGitBranching/)
|
1085 | - [初级教程](http://rogerdudler.github.io/git-guide/index.zh.html)
|
1086 | - [廖雪峰的Git教程](http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)
|
1087 | - [蒋鑫老师将带你入github的大门](http://www.worldhello.net/gotgithub/)
|
1088 | - [git详解](http://www.open-open.com/lib/view/open1328069609436.html)
|
1089 | - [oschina教程](http://git.oschina.net/progit/)
|
1090 | - [How to undo (almost) anything with Git撤销一切,汇总各种回滚撤销的场景,加强学习。](https://github.com/blog/2019-how-to-undo-almost-anything-with-git)
|
1091 | - [Git 教程 | 菜鸟教程runoob.com](http://www.runoob.com/git/git-tutorial.html)
|
1092 | - [Git 本地仓库和裸仓库](https://gold.xitu.io/post/5842f9b861ff4b005889ade6)
|
1093 | - [沉浸式学 Git](http://www.kancloud.cn/kancloud/igit/46710)
|
1094 | - [Git进阶用法,主要是rebase高级用法](http://way.oschina.io/2016/12/15/notes/GitAdvance/?utm_source=gank.io&utm_medium=email)
|
1095 |
|
1096 |
|