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