Git基本使用

255 阅读16分钟

以下内容转自廖雪峰 和《GitHub入门与实践》

0.1 Git是什么?


Git是目前世界上最先进的分布式版本控制系统
工作原理 / 流程:

  • Workspace:工作区

  • Index / Stage:暂存区

  • Repository:仓库区(或本地仓库)

  • Remote:远程仓库

0.2 集中式VS分布式


SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就纳闷了。

Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

0.3 安装Git


...自行百度

1.0 廖大大の小结


创建版本库
初始化一个Git仓库,使用git init命令。
添加文件到Git仓库,分两步:

  • 使用命令git add <file>,注意,可反复多次使用,添加多个文件;
  • 使用命令git commit -m <message>,完成。

时光机穿梭

  • 要随时掌握工作区的状态,使用git status命令。
  • 如果git status告诉你有文件被修改过,用git diff可以查看修改内容。

版本回退

  • HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id
  • 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
  • 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。

撤销修改

  • 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- <file>
  • 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。
  • 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,用git reset --hard HEAD^回退到上一个版本,不过前提是没有推送到远程库。

删除文件

  • 一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm命令删了:
  • 现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit
  • 另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本。
  • git checkout --<file>其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

  • 注意: 从来没有被添加到版本库就被删除的文件,是无法恢复的!
  • 小提示: 先手动删除文件,然后使用git rm <file>git add<file>效果是一样的。(先删除文件再用git rm <file>相当于把commit的文件从版本库退回到暂存区,标上delete记号,之后再commit就会从版本库删除)

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

创建与合并分支

  • 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>/ git branch -D <name>

解决冲突

  • 当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 apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除) ,回到工作现场;
  • 在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>命令,把bug提交的修改“复制”到当前分支,避免重复劳动。
  • 可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,例如用命令:git stash apply stash@{0}

Feature分支

  • 开发一个新feature,最好新建一个分支;
  • 如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>强行删除。

多人协作

  • 多人协作的工作模式通常是这样:
  • 首先,可以试图用git push origin <branch-name>推送自己的修改;
  • 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
  • 如果合并有冲突,则解决冲突,并在本地提交;
  • 没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!
  • 如果git pull提示 no tracking information ,则说明本地分支和远程分支的链接关系没有创建,用命令 git branch --set-upstream-to <branch-name> origin/<branch-name>

  • 查看远程仓库信息,使用git remote,或者使用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 没看懂,碰到了再去查看
  • 添加远程库 要关联一个远程库,使用命令git remote add origin git@github.com:5-seasons/git-tutorial.git
    关联后,使用命令git push -u origin master第一次推送master分支的所有内容;
    此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
    分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!
  • 从远程库克隆
    要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。
    Git支持多种协议,包括https,但ssh协议速度最快。

1.1 基本操作


  • git init - 初始化仓库
  • git status - 查看仓库的状态
  • git add - 向暂存区中添加文件
  • git commit - 保存仓库的历史记录
  • git log - 查看提交日志
  • git diff - 查看更改前后的差别
  • git init : 初始化仓库
  • git status : 查看仓库的状态

新建一个文件'jk.md'后执行git status:

Untracked files:未跟踪的文件

  • git add jk.md :向暂存区中添加文件
    执行 git add jk.md命令后,没有任何显示,这就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。再执行git status

    Changes to be committed:要提交的更改

  • git commit -m 'jk' :保存仓库的历史记录 执行 git commit -m 'jk' (记述一行提交信息)后再执行git status

    没什么要提交的,工作树(目录)是干净的
    git commit也可以不添加-m,直接执行git commit记述详细提交信息)后会启动编辑器,并显示如下:( 不建议这么做

  1. 按键盘字母 i 进入insert(插入)模式
  2. 修改最上面那行黄色合并信息,可以不修改(终止提交:提交信息留空并关闭编辑器)
  3. 按键盘左上角"Esc"
  4. 输入":wq",注意是冒号和wq,按回车键即可

在编辑器中记述提交信息的格式如下:

  • 第一行:用一行文字简述提交的更改内容
  • 第二行:空行
  • 第三行以后:记述更改的原因和详细内容

在'jk.md'中写入一些内容再执行git status Changes not staged for commit:更改没有暂存以提交

  • git diff :查看更改前后的差别 此时执行 git diff : 查看当前工作树与暂存区的差别

    由于我们尚未用git add命令向暂存区添加任何东西,所以程序只会显示工作树与最新提交状态之间的差别。("+"号标出的是新添加的行;"-"号标出的是被删除的行)

执行git add jk.md后执行git status和之前一样;

若执行git diff,由于工作树和暂存区的状态并无差别,结果什么都不会显示。
执行git diff HEAD查看与最新提交的差别 (此时和上面的git diff一样)

  • git log :查看提交日志 例如:第一次修改写入v1,commit;第二次修改写入v2,commit;第三次修改写入v3,commit。
  • 执行 git log查看提交日志

git log可以查看以往仓库中提交的日志。包括可以查看什么人在什么时候进行了提交或合并,以及操作前后有怎样的差别
commit栏旁边显示的是指向这个提交的40位16进制的哈希值commit id(版本号)

  • 执行git log --pretty=short : 只显示提交信息的第一行

如果只想让程序显示第一行的简述信息,可以在git log后加上--pretty=oneline参数

  • 执行git log <file> : 只显示指定目录、文件的日志

git log后加上目录名,便会只显示该目录下的日志;如果加的是文件名,就会只显示与该文件相关的日志

  • 执行git log -p:显示文件的改动

执行git log -p <file> :可以只查看指定文件的提交日志以及提交前后的差别

  • 执行git log --graph : 以图表形式查看提交、分支非常直观

  • 执行git reflog : 查看当前仓库执行过的操作的日志

1.2 分支的操作


  • git branch - 显示分支一览表
  • git checkout -b/git switch -c:创建、切换分支
  • 特性分支 Topic
  • 主干分支
  • git merge - 合并分支
  • git log --graph - 以图表形式查看分支
  • git branch : 显示分支一览表
    可以看到master分支左侧由 "*" (星号),表示这是我们当前所在的分支

  • git checkout -b/git switch -c : (建议使用switch
    实际上相当于连续执行git branch A git checkout A/git branch B git switch B 执行git checkout/switch -:切换回上一个分支 (一直执行就会在最近两个分支行中不停切换)

  • git merge :合并分支
    通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
    如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。执行git merge --no-ff:表示禁用Fast forward
    git merge --no-ff -m "merge with no-ff" A:因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。

  • git branch -d : 删除分支
    如果要删除一个已修改提交但未合并的 Feacure分支

    销毁失败。Git友情提醒:分支A还没有被完全合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数。

  • git log --graph : 以图表形式查看分支

1.3 更改提交的操作


  • git reset --hard - 回溯历史版本
  • 消除冲突
  • git commit --amend - 修改提交信息
  • git rebase -i - 压缩历史
  • git reset --hard :回溯历史版本
    首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交1094adb...(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100
    使用git reset --hard HEAD^,或者提供目标地点的哈希值(只要输入四位以上就可以执行)。

HEAD严格来说不是指向提交,而是指向 当前分支 master,master才是指向当前分支中 最新提交 的,所以,HEAD指向的就是当前分支。

执行 git reflog :查看当前仓库执行过的操作的日志

只要不进行Git的GC(Garbage Collection,垃圾回收),就可以通过日志随意调取近期的历史状态
回溯版本之后可以通过git reflog在日志中找出回溯历史之前的哈希值(7位),通过git reset --hard命令恢复到回溯历史前的状态

  • 消除冲突

    这时,系统告诉我们jk.md文件发生了冲突(Conflict)。可以打开jk.md文件如下:

    选择保留双方更改,解决冲突后,执行git add命令与git commit命令。

实际开发中,往往需要删除其一,所以各位在处理冲突时,务必仔细分析冲突部分的内容再行修改

  • git commit --amend : 修改(修正)提交信息
    执行上面的命令后,编辑器就会启动,修改提交信息

  • git rebase -i :压缩历史
    创建一个新的分支C:故意写入错误字词"邓紫琪",然后执行git add jk.mdgit commit -m 'GEM'或者直接用一个命令完成:git commit -am 'GEM' 。现在发现之前写错了,又改成"邓紫棋",然后在提交git commit -am 'Fix typo'(修复错误)。实际上,我们不希望在历史记录中看到这类提交,因为健全的历史记录并不需要它们。如果能在最初提交之前就发现并修正这些错误,也就不会出现这类提交了。Now,我们来更改历史。将'Fix typo'修正的内容与之前一次的提交合并,在历史记录中合并为一条完美的提交。

执行git rebase -i HEAD~2,进入编辑器,将第二次的commit id的 左侧的pick 改为 fixup
这样一来, Fix typo 就从历史中被抹去,也就相当于 GEM中从来没有出现过错别字。这也算是一种良性的历史改写。

1.4 推送至远程仓库


  • git remote add - 添加远程仓库
  • git push - 推送至远程仓库
  • git remote add : 添加远程仓库 执行git remote add origin git@github.com:5-seasons/git-tutorial.git
    注意 ,把上面的5-seasons替换成你自己的GitHub账户名。

执行后Git会自动将远程仓库的名称设置为origin(标识符)。此时,本地仓库和远程仓库就实现了关联

下一步,就可以把本地仓库的所有内容推送到远程库上:

  • git push :推送至远程仓库 把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
    执行git push -u origin master ,当然,也可以推送到其他分支, 如git push -u origin dev

由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

推送成功后,可以立刻在GitHub页面中看到远程库的内容已经和本地一模一样。

从现在起,只要本地作了提交,就可以通过命令:

git push origin master

把本地master分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!

git push 命令用于从将本地的分支版本上传到远程并合并。 命令格式如下:

git push <远程主机名> <本地分支名>:<远程分支名>

如果本地分支名与远程分支名相同(同名形式),则可以省略冒号:

git push <远程主机名> <本地分支名>

此时,当前分支的内容就会被推送给远程仓库originmaster分支;-u参数可以在推送的同时,将origin仓库的master分支设置为本地仓库当前分支的 upstream (上游)

1.5 从远程仓库获取


  • git clone - 获取远程仓库
  • git pull - 获取最新的远程仓库分支
  • git clone : 获取远程仓库

  • 获取远程仓库
    执行git clone git@github.com:5-seasons/git-tutorial.git

执行命令后,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin默认只会获取远程的master分支。 可以用git branch命令查看:

或者使用git branch -a(添加-a参数可以同时显示本地仓库和远程仓库的分支信息):

  • 获取远程的dev分支

执行git checkout -b dev origin/dev

-b参数后面是本地仓库中新建分支的名称,新建分支名称后面是获取来源的分支名称。即以名为origin的仓库(这里指GitHub端的仓库)的dev分支为来源,在本地仓库中创建dev分支。

  • git pull :获取最新的远程仓库分支
    git pull 其实就是 git fetchgit merge FETCH_HEAD 的简写。