最近工作中需要用到git,就把git重新复习了一下做了个总结。
git基础
在已存在目录中初始化仓库
$ git init
该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。
如果在一个已存在文件的文件夹(而非空文件夹)中进行版本控制,你应该开始追踪这些文件并进行初始提交。 可以通过 git add 命令来指定所需的文件来进行追踪,然后执行 git commit
克隆现有仓库
$git clone <url>
默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来.
如果你想在克隆的时候,自定义本地仓库名称:
$git clone <url> <filename>
你工作目录下的每一个文件都不外乎这两种状态:已跟踪 或 未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后, 它们的状态可能是未修改,已修改或已放入暂存区。简而言之,已跟踪的文件就是 Git 已经知道的文件。
编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复
image.png
检查当前文件状态
$git status
通过该命令可以查看所有文件状态,eg:新创建未跟踪、上次快照后修改的、修改后添加到暂存的。。。
我们在新建的项目里面,使用完git init以后,执行git status:
忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件
文件 .gitignore 的格式规范如下:
所有空行或者以 # 开头的行都会被 Git 忽略。
可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
匹配模式可以以(/)开头防止递归。
匹配模式可以以(/)结尾指定目录。
要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号()匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符 (这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c); 问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符, 表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 使用两个星号(* )表示匹配任意中间目录,比如 a/**/z 可以匹配 a/z 、 a/b/z 或 a/b/c/z 等。
跟踪新文件
$git add <files>
通过这个命令,可以将新创建或者跟踪后已修改的文件放入暂存中,如果此时提交commit,那么该文件在你运行git add时的版本将被留存在后续历史记录中。
跟踪新创建的文件
我们在刚刚项目中执行一下git add命令
image.png
你会发现app目录下的所有文件都被跟踪了。
git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件。
暂存已修改的文件
刚刚我们将.gitignore进行暂存,我们用cat命令看下它的内容:
image.png
里面的内容只有gradle/,我们修改以后再查看下状态
image.png
再次使用git add .gitignore就可以将修改后的.gitignore进行暂存。
查看已暂存和未暂存的修改
$git diff
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff:
image.png
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。
若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff --staged 命令。 这条命令将比对已暂存文件与最后一次提交的文件差异
提交更新
$git commit
执行完这个命令会进入vim编辑模式,输入i进入insert模式可以进行编辑,输入提交信息,编辑完成按esc后退出insert模式,
1.保存内容退出:输入:wq然后按“回车”键进行保存退出(也可以切换到大写模式,直接输入ZZ)
2.未修改内容直接退出:输入:q
3.修改过不想保存退出:输入:q!
4.强制退出:输入:!
另外,你也可以在 commit 命令后添加 -m 选项,将提交信息与命令放在同一行:$git commit -m <message>
$git commit -a
这个命令会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
移除文件
$git rm <filenames>
如果想将暂存区文件移除(commit以后未修改过),即不跟踪该文件,可以使用该命令。
如果修改过放在暂存区,则需要使用强制删除选项-f
即 $git rm -f <filenames>
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中,我们可以使用--cached选项:
$ git rm --cached <filenames>
git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式
移动文件
$git mv <oldname> <newname>
git mv和mv的区别
查看提交历史
$git log
刚刚我们提交过几次,我们执行完git log看看输出什么:
$git log -p -2
显示最近的两次提交所引入的差异(按 补丁 的格式输出).
另一个非常有用的选项是 --pretty。 这个选项可以使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如 oneline 会将每个提交放在一行显示,在浏览大量的提交时非常有用。 另外还有 short,full 和 fuller 选项,它们展示信息的格式基本一致,但是详尽程度不一:
$ git log --pretty=oneline
下表列出了我们目前涉及到的和没涉及到的选项,以及它们是如何影响 log 命令的输出的:
image.png
image.png
取消暂存的文件
你已经修改了两个文件并且想要将它们作为两次独立的修改提交, 但是却意外地输入 git add * 暂存了它们两个。如何只取消暂存两个中的一个呢?
$git reset HEAD <file>
撤消对文件的修改
如果你并不想保留对 CONTRIBUTING.md 文件的修改怎么办? 你该如何方便地撤消修改——将它还原成上次提交时的样子(或者刚克隆完的样子,或者刚把它放入工作目录时的样子)?
$ git checkout -- <filenames>
查看远程仓库
$git remote
它会列出你指定的每一个远程服务器的简写。 如果你已经克隆了自己的仓库,那么至少应该能看到 origin ——这是 Git 给你克隆的仓库服务器的默认名字
$git remote -v
你也可以指定选项 -v,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。
image.png
添加远程仓库
$git remote add <shortname> <url>
添加一个新的远程 Git 仓库,同时指定一个方便使用的简写
image.png
从远程仓库中抓取与拉取
$ git fetch <remote>
这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
必须注意 git fetch 命令只会将数据下载到你的本地仓库——它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。
$git fetch <远程仓库名称> <远程分支>
抓取远程仓库的某个分支到本地
$git pull
如果你的当前分支设置了跟踪远程分支(git branch --set-upstream-to=<仓库服务器,默认名字origin>/<分支> <本地分支> ), 那么可以用 git pull 命令来自动抓取后合并该远程分支到当前分支
推送到远程仓库
$ git push <远程服务器,默认origin> <branch>
只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。 当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先抓取他们的工作并将其合并进你的工作后才能推送。
如果你在本地新建了分支,可以通过这种方式推送到远程仓库并建立同名的远程分支,并自动跟踪该远程分支。
查看某个远程仓库
$git remote show <远程仓库名称>
这个命令列出了当你在特定的分支上执行 git push 会自动地推送到哪一个远程分支。 它也同样地列出了哪些远程分支不在你的本地,哪些远程分支已经从服务器上移除了, 还有当你执行 git pull 时哪些本地分支可以与它跟踪的远程分支自动合并。
远程仓库的重命名与移除
$git remote rename <oldName> <newName>
$ git remote remove paul
打标签
$git tag -a <标签内容> -m <提交内容>
如果你想在提交之后打标签,可以使用
$git tag -a <标签内容> <提交的校验和>
共享标签
$git push <远程仓库名字> <tag名字>
默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。
如果想要一次性推送很多标签,也可以使用带有 --tags 选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里。
$ git push origin --tags
删除标签
$git tag -d <tagname>
可以删除本地标签,如果删除远程标签应该使用
$ git push <远程仓库name> --delete <tagname>
git分支
Git 会保存一个提交对象(commit object)。 知道了 Git 保存数据的方式,我们可以很自然的想到——该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象, 而由多个分支合并产生的提交对象有多个父对象.
我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和(使用我们在 起步 中提到的 SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中 (Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:
$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'
当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和, 然后在 Git 仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象, 它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。 如此一来,Git 就可以在需要的时候重现此次保存的快照。
现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个 树 对象 (记录着目录结构和 blob 对象索引)以及一个 提交 对象(包含着指向前述树对象的指针和所有提交信息)。
image.png
首次提交对象及其树结构
做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。
image.png
分支创建
$git branch <分支名称>
这会在当前所在的提交对象上创建一个指针。
image.png
那么Git怎么知道当前在哪一个分支上呢?
它有一个HEAD的特殊指针,指向所在的本地分支
image.png
分支切换
$git checkout <branchname>
这样HEAD就指向了branchname的分支
如果我们此时修改了文件,进行了一次提交
image.png
这个时候我们发现,master分支还是指向了checkout发生时的对象,而testing分支向前移动了。
如果这个时候你切换回master,然后做了一些修改然后提交,那么这个项目的提交历史就会产生分叉
image.png
可以使用git log --graph --all查看分叉历史
创建新分支的同时切换过去
通常我们会在创建一个新分支后立即切换过去,这可以用 git checkout -b <newbranchname> 一条命令搞定。
分支的创建与合并
如果我们现在只有一个master主分支的话,那么我们项目的指针应该是这样的:
image.png
如果我们需要开发一个新功能#iss53,那么我们首先应在在master的基础上新建一个iss53的分支
image.png
对应的指针指向应该是这样:
image.png
然后我们修改该分支文件进行相应功能开发,在此过程中,iss53 分支在不断的向前推进,也就是说,你的 HEAD 指针指向了 iss53 分支
image.png image.png
但是这个时候我们突然收到线上有个bug需要紧急修复,那么我们需要在之前master分支的基础上进行修复。我们这个时候需要先切换回master分支,但是之前我们应该先将iss53分支提交:
image.png
这个时候对应的项目应该是这样的:
image.png
现在问题解决,我们需要把hotfix分支合并到master然后发布:
image.png
在合并的时候,你应该注意到了“快进(fast-forward)”这个词。 由于你想要合并的分支 hotfix 所指向的提交 C4 是你所在的提交 C2 的直接后继, 因此 Git 会直接将指针向前移动。换句话说,当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。
现在最新的修改已经在master分支所指向的快照中:
image.png
此时你已经将问题修复,那么你可以将hotfix删除,然后切换到iss53分支继续开发:
image.png
这个时候项目的指针又发生了变化:
image.png
如果我们此时完成了iss53需求的开发,需要将代码合并到master,如果我们没有在hotfix和iss53修改同一行代码,这个时候和hotfix合并到meter一样:
image.png
和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
image.png
遇到冲突时候到合并
如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关 hotfix 分支的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突:
image.png
这个时候我们需要在当前快照即c6上,手动合并当前产生产生冲突的文件,然后进行暂存和提交:
image.png
查询已合并分支
$git branch --merged
查询已合并分支
$ git branch --no-merge
参考:部分内容和图片来自https://www.git-scm.com/book/en/v2
网友评论