git学习

Git学习

创建版本库

1
2
3
4
5
6
7
8
9
$ mkdir learngit//创建目录
$ cd learngit//切换目录
$ pwd//显示当前目录
$ ls -ah//显示目录
$ git init//把目录变成可管理的仓库
$ git add //把文件添加到仓库
$ git commit -m""//git commit告诉Git,把文件提交到仓库,-m用于添加对这次操作的说明
$ cat readme.txt//查看readme.txt的内容
/Users/michael/learngit

在Windows中当输入完txt内容后,按esc加:wq退出输入模式

时光机穿梭

1
2
3
$ git status 命令可以让我们时刻掌握仓库当前的状态
$ git diff顾名思义就是查看difference,显示的格式正是Unix通用的diff格式
提交修改和提交新文件步骤一样,$ git add $ git commit;不过在执行第二步之前可以运行 git status 看看当前仓库的状态

版本回退

git log命令显示从最近到最远的提交日志,如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数

我们要把当前版本append GPL回退到上一个版本add distributed,就可以使用git reset命令

首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交1094adb...(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

1
$ git reset --hard HEAD^

最新的那个版本append GPL已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办?

办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个append GPLcommit id1094adb...,于是就可以指定回到未来的某个版本

版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?

在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^回退到add distributed版本时,再想恢复到append GPL,就必须找到append GPL的commit id。Git提供了一个命令git reflog用来记录你的每一次命令:

工作区和暂存区

git add命令实际上就是把要提交的所有修改(来自工作区的修改)放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。

管理修改

Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。

提交后,用git diff HEAD -- readme.txt命令可以查看工作区和版本库里面最新版本的区别:

每次修改,如果不用git add到暂存区,那就不会加入到commit

撤销修改

命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:

一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

总之,就是让这个文件回到最近一次git commitgit add时的状态。

git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout命令。

Git同样告诉我们,用命令git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file

场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

删除文件

在Git中,删除也是一个修改操作,我们实战一下,先添加一个新文件test.txt到Git并且提交

一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm命令删了:

这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了

现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit

另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本

git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

小提示:先手动删除文件,然后使用git rm 和git add效果是一样的。

1
$ git checkout -- test.txt

git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!

命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容

远程仓库

1.创建自己的SSH Key

1
$ ssh-keygen -t rsa -C "youremail@example.com"

如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsaid_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

第2步:登陆GitHub,打开“Account settings”,“SSH Keys”页面:

然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:

添加远程库

要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git

关联一个远程库时必须给远程库指定一个名字,origin是默认习惯命名;

关联后,使用命令git push -u origin master第一次推送master分支的所有内容;

此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;

分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!

从远程库克隆

要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。

Git支持多种协议,包括https,但ssh协议速度最快。

1
$ git clone git@github.com:michaelliao/gitskills.git

分支管理

创建并合并分支

Git鼓励大量使用分支:

查看分支:git branch

创建分支:git branch <name>

切换分支:git checkout <name>或者git switch <name>

创建+切换分支:git checkout -b <name>或者git switch -c <name>

合并某分支到当前分支:git merge <name>

删除分支:git branch -d <name>

解决冲突

冲突来源:master分支和feature1分支各自都分别有新的提交,变成了这样

当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。

解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。

git log --graph命令可以看到分支合并图。

分支管理策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

Git分支十分强大,在团队开发中应该充分应用。

合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并

Bug分支

修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场;工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看

在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>命令,把bug提交的修改“复制”到当前分支,避免重复劳动。

Feature分支

开发一个新feature,最好新建一个分支;

如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>强行删除。

多人协作

推送分支

1
$ git push origin <分支名>
  • master分支是主分支,因此要时刻与远程同步;
  • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
  • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
  • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

抓取分支

1
$ git pull//抓取分支

多人协作工作模式

  1. 首先,可以试图用git push origin <branch-name>推送自己的修改;
  2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
  3. 如果合并有冲突,则解决冲突,并在本地提交;
  4. 没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!
  5. 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
1
2
3
4
5
6
7
8
9
10
$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

git branch --set-upstream-to=origin/<branch> dev

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置devorigin/dev的链接:

1
2
$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再pull:

1
2
3
4
$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.

小结

  • 查看远程库信息,使用git remote -v

  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;

  • 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;

  • 在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;

  • 建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name

  • 从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

    Rebase

    • rebase操作可以把本地未push的分叉提交历史整理成直线;

    • rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比

      指令是

      1
      $ git rebase

      标签管理

      发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。

      Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。

      。tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起

      创造标签

      • 标签不是按时间顺序列出,而是按字母排序的

      • 命令git tag <tagname>用于新建一个标签,默认为HEAD,也可以指定一个commit id;

      • 命令git tag -a <tagname> -m "blablabla..."可以指定标签信息;

      • 命令git tag可以查看所有标签。

      • 注意:标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。

      #### 操作标签

      • 命令git push origin <tagname>可以推送一个本地标签;
      • 命令git push origin --tags可以推送全部未推送过的本地标签;
      • 命令git tag -d <tagname>可以删除一个本地标签;
      • 命令git push origin :refs/tags/<tagname>可以删除一个远程标签