版本控制

即控制版本,帮助控制(或管理)某事物(通常是源代码)的不同版本。(保留项目的详细历史记录)

两大类型

_Git_就是分布式的版本控制工具

_GitHub_则是托管Git项目的服务

关键术语

版本控制系统(VCS):管理源代码不同版本的工具;源代码管理器(SCM):版本控制系统的另一个名称。Git是一个SCM,也是一个VCS,它的网站是https://git-scm.com/

提交(Commit):Git将数据看作微型文件系统中的一组快照。每次commit,都对文件当时的状态拍照,并保存该快照的引用。commit是Git中的基本单位。

仓库(Repository/repo):一个包含项目内容和几个文件(在Mac OS上默认处于隐藏状态)的目录,可以存储在本地,或者作为远程副本存储在其他计算机上,由commit构成。

工作目录(Working Directory):在计算机的文件系统中可以看到的文件,当在代码编辑器中打开项目文件时,正是在工作目录中处理文件。与这些文件形成对比的是保存在仓库里的文件。

检出(Checkout):将仓库中的内容复制到工作目录下。

暂存区/暂存索引(Staging Area/Staging Index):Git目录下的一个文件,存储即将进入下一个commit内容的信息。可将其看作准备工作台,Git将在此获取下一个commit。

SHA(Secure Hash Algorithm,安全哈希算法):由0~9、a~f组成的长度为40的字符串,根据Git中文件或目录结构的内容得出,是每个commit的ID编号,示例:e2adf8ae3e2e4ed40add75cc44cf9d0a869afeb6

分支(Branch):从主开发流程中分支出来的新的开发流程。这种分支开发流程可以在不更改主流程的情况下继续延伸下去。

Git命令

git init在计算机上从头创建全新仓库。运行该命令会初始化Git跟踪所有内容会用到的所有必要文件和目录。所有这些文件存储在.git目录(在Mac/Linux上将是一个隐藏目录)下,该目录就是一个库,Git将所有commit记录在这里,并跟踪所有内容。.git目录下各项内容:

git clone将一个现有仓库从其他地方克隆或复制到本地计算机上。该命令后传入要克隆的Git仓库的路径(通常是URL),示例:git clone https://github.com/udacity/course-git-blog-project。默认创建一个与被克隆仓库名称相同的目录,也可以再提供一个参数作为该目录的名称。

git status查看仓库当前状态。输出结构将有所不同,具体取决于文件是否被添加/删除/修改、暂存索引的情况,以及仓库的状态。

git log显示有关所有commit的信息。默认情况下,显示每个commit的:

--oneline选项用于更改git log显示信息的方式:

--stat(stat是statistics 统计信息的简称)选项用于更改git log显示信息的方式:

-p--patch的简写)选项用于更改git log显示信息的方式:

命令行分页器/less的使用:

git show显示有关指定commit的信息。输出和git log -p完全一样,默认情况下,显示:

git add将文件从工作目录添加到暂存区。

git commit将文件从暂存区取出,并保存到仓库,将打开配置中指定的编辑器,在编辑器中:

-m选项绕过编辑器。如果提交说明很简短,不想打开编辑器输入信息,可以直接使用该选项传入信息,示例:git commit -m "Initial commit",但是不能再为commit提供信息的描述了。

每个commit都有其_侧重点_,应该记录一项更改,这并不限制添加/删除多少行代码或添加/删除/修改多少个文件,一个commit不应该包含不相关的更改。

编写良好的提交说明,需要注意:

如果需要解释为何进行了提交,可以第一行是消息本身,之后空出一行,然后输入正文/说明,包括为何commit的详情(例如URL)。最重要的是,保持一致性

git diff显示文件两个版本之间的差异,用来查看已被加入但尚未提交的更改。git log -p在后台使用了该命令。

如果想将某个文件保留在项目的目录结构中,又要确保不被意外地提交到项目中去,可以使用名称特殊的文件.gitignore(Git会查看其中内容)。将此文件添加到项目的根目录,并在其中列出Git忽略/不跟踪的文件名。

通配符允许使用特殊字符来表示某些格式/字符,在.gitignore文件中可以使用:

git tag为特定提交添加标签。标签是提交的额外标记,可以指示有用的信息,比如v1.0。git tag -a v1.0命令中的-a选项告诉Git创建一个带注释的标签,将打开编辑器,并等待为标签输入注释信息。如果没有提供该选项,将创建一个轻量级标签。建议使用带注释的标签,因为它包含大量额外信息,如:

只输入git tag时,命令行会显示仓库中所有的标签。标签与commit相绑定,想知道它在仓库中的具体位置时,可以使用git log

-d选项删除标签,如:git tag -d v1.0

git tag -a v1.0 a87984将向SHA为a87984的commit添加标签v1.0。借助这一技巧,可以为仓库中的任一commit添加标签。

git branch与Git分支进行交互(列出、创建、删除)。用于并行开发项目的不同功能,不会对哪些提交属于哪些功能而感到困惑。

只输入git branch,将列出仓库中所有分支。要创建分支,只需git branch后面加上要创建的分支的名称(默认分支名为:master)。

git log输出的结果中,“HEAD”具有一个指向_当前分支_的箭头。git branch命令输出的结果中,_活跃分支_旁有一个“*”。

-d选项告诉Git删掉给出的分支,示例:git branch -d sidebar。注意,无法删除当前所在的分支。如果某个分支上有任何其他分支上都没有包含的commit(即此commit是要被删除的分支所独有的),Git不会删除该分支。要强制删除,需要使用-D选项。

git checkout在不同的分支和标签之间进行切换。

在进行commit时,该commit将添加到当前分支上。要在分支之间切换,需要使用git checkout。该命令的工作方式:

  1. 从工作目录中删除Git跟踪的所有文件和目录(这些文件和目录都存储在仓库中,故不会丢失)
  2. 转到仓库,提取分支指向的commit所对应的所有文件和目录

添加-b选项,能够创建分支并切换到该分支。示例:git checkout -b footer master

git log --oneline --graph --all将显示仓库中所有分支和commit。--graph选项将条目和行添加到输出的最左侧,显示实际的分支;--all选项会显示仓库中所有的分支。

git merge将不同分支的更改自动合并在一起。发生合并时,Git将:

合并时,将其他分支合并到当前(检出的)分支上,不是将两个分支合并到一个新的分支上,也不是将当前分支合并到其他分支上,另一分支上的更改将出现在当前(检出的)分支上。

快进合并(Fast-forward merge):将使当前(检出的)分支向前移动,直到它指向与另一个分支指向的commit一样为止。

普通合并:合并的是两个完全不一样的分支,将提交commit。在进行commit时,需要提供commit消息。因为这是合并commit,因此已经提供默认消息,可以更改此消息,但通常会直接使用默认消息(当编辑器被打开并包含此消息时,直接关闭以确认使用该commit消息)。

有时候Git无法完全自动地进行合并,合并失败时,就称合并冲突。Git将尝试尽可能合并多的内容,然后将留下特殊选项(例如>>><<<),告诉程序员需要从何处手动修复。

Git会跟踪文件中的代码行,如果相同的行在不同的分支上都被更改了,将产生合并冲突。

合并冲突指示符:

Git使用合并冲突指示符来告知两个不同分支上哪些行产生了合并冲突,以及原始的行是什么。要解决合并冲突,需要:

  1. 选择保留哪些行
  2. 删掉所有带指示符的行

之后,直接保存文件,并将其添加到暂存区,然后commit。就像普通合并一样,编辑器会弹出,让提供commit消息……

git commit --amend更改最后一个commit,如果在提交中忘记包含某个文件或在提交说明中打错了字,就需要调用该命令。

git revert向Git提供需要还原的commit的SHA,就能撤销在该提交中作出的更改,Git将创建新的commit,执行完全相反的更改。

git reset删除提交,不能随意删除任意提交,必须按顺序删除。此命令存在潜在危险,它会从仓库中删除提交。

Git会在完全清除任何内容之前,持续跟踪大约30天。要调用这些内容,需要使用git reflog命令。

可以使用特殊的“祖先引用”字符告诉Git相对引用:

引用之前的commit:

^的区别主要体现在通过合并而创建的commit中。合并commit具有两个父级,^表示第一个父commit(运行git merge所在的分支),^2表示第二个父commit(被合并的分支)。

git reset <reference-to-commit>

Git根据所使用选项来判断是清除、暂存之前commit的更改,还是取消暂存之前commit的更改。

HEAD指向9ec05ca上的master:

在进行任何重置操作之前,通常在最近的commit上创建backup分支,如果出现错误,可以返回这些commit

小结

git remote管理远程仓库并与之交互

运行此命令时,如果尚未配置远程仓库,将不显示任何内容;如果克隆仓库后,将自动获得一个远程仓库。

origin是远程仓库所在位置的简略表示,可以将它重命名为别的名字,但通常被命名为此名称。使用-v选项,查看远程仓库的完整路径。

git remote add <name> <url>将本地仓库与远程仓库建立连接

git push <remote-shortname> <branch>将本地commits推送到远程仓库

注意:

跟踪分支的名称(origin/master)包含远程仓库的简写名及分支名称,可在本地仓库跟踪远程仓库的信息,但并不能实时表现被跟踪分支在远程仓库上的位置(当其他人对远程仓库做了更改时)。

git pull <remote-shortname> <branch>拉取远程仓库的更改

git fetch <remote-shortname> <branch>用于从远程仓库分支检索commit,但不会在收到这些commit之后,自动将本地分支与远程跟踪分支合并。可以将 git fetch 想象成 git pull 它的一半操作,而 git pull 的另一半是合并。

使用git fetch的一个主要情形是当远程分支和本地分支都拥有对方所没有的更改。要获取远程更改,将它们存储到本地分支中,然后手动执行合并。最后,可以将新的合并commit推送会远程仓库。

参考链接

https://cn.udacity.com/course/version-control-with-git–ud123