前言:这段时间,打算学习一下git的操作,了解一下原理,而不是仅仅局限于一些基础的操作,特在此记录一下。
2.1Git基础–获取Git仓库目的:能够配置并初始化一个仓库(repository)、开始或停止跟踪(track)文件、暂存(stage)或提交(commit)更改。
获取Git仓库
通常有两种获取Git项目仓库的方式:
1.将尚未进行版本控制的本地目录转换为Git仓库;
2.从其他服务器克隆一个已存在的Git仓库;
两种方式都会在你的本地机器上得到一个工作就绪的Git仓库。
在已存在目录中初始化仓库
如果你有一个尚未进行版本控制的项目目录,想要用 Git 来控制它,那么首先需要进入该项目目录中。 如果你还没这样做过,那么不同系统上的做法有些不同:
之后执行:
$ git init
该命令将创建一个名为.git的子目录,这个子目录含有你初始化的Git仓库中所有的必须文件,这些文件是Git仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。
如果在一个已存在文件的文件夹(而非空文件夹)中进行版本控制,你应该开始追踪这些文件并进行初始提交。可以通过git add命令来指定所需的文件进行追踪,然后执行git commit :
$ git add *.c $ git add LICENSE $ git commit -m 'initial project version'
现在,你已经得到了一个存在被追踪文件与初始提交的Git仓库。
克隆现有的仓库
如果你想获得一份已经存在了的 Git 仓库的拷贝,这时就要用到 git clone 命令。Git 克隆的是该 Git 仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件。 当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。 事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库 (虽然可能会丢失某些服务器端的钩子(hook)设置,但是所有版本的数据仍在 )。
克隆仓库的命令是 git clone 。 比如,要克隆 Git 的链接库 libgit2,可以用下面的命令:
$ git clone https://github.com/libgit2/libgit2
这会在当前目录下创建一个名为 “libgit2” 的目录,并在这个目录下初始化一个 .git 文件夹, 从远程仓库拉取下所有数据放入 .git 文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的 libgit2 文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。
如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以通过额外的参数指定新的目录名:
$ git clone https://github.com/libgit2/libgit2 mylibgit
这会执行与上一条命令相同的操作,但目标目录名变为了mylibgit 。
Git 支持多种数据传输协议。 上面的例子使用的是 https:// 协议,不过你也可以使用 git:// 协议或者使用 SSH 传输协议,比如 user@server:path/to/repo.git 。
检查当前文件状态
可以用git status命令查看哪些文件处于什么状态。
跟踪新文件
使用命令git add开始跟踪一个文件。所以,要跟踪README文件,运行:
$ git add README
此时,再运行git status命令,会看到README文件已被跟踪,并处于暂存状态:
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git restore --staged..." to unstage) new file: README
只要在Changes to be committed这行下面的,就说明是已暂存状态。
暂存已修改的文件
现在我们来修改一个已被跟踪的文件。如果你修改了一个名为CONTRIBUTING.md的已被跟踪的文件,然后运行git status命令,会看到下面内容:
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD..." to unstage) new file: README Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: CONTRIBUTING.md
文件 CONTRIBUTING.md 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。 要暂存这次更新,需要运行 git add 命令。
现在让我们运行 git add 将“CONTRIBUTING.md”放到暂存区,然后再看看 git status 的输出:
$ git add CONTRIBUTING.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD..." to unstage) new file: README modified: CONTRIBUTING.md
现在两个文件都已暂存,下次提交时就会一并记录到仓库。
状态简览
git status命令的输出十分详细,但其用语有些繁琐。 Git 有一个选项可以帮你缩短状态命令的输出,这样可以以简洁的方式查看更改。 如果你使用 git status -s 命令或 git status --short 命令,你将得到一种格式更为紧凑的输出。
$ git status -s M README MM Rakefile A lib/git.rb M lib/simplegit.rb ?? LICENSE.txt
新添加的未跟踪文件前面有 ?? 标记,新添加到暂存区中的文件前面有 A 标记,修改过的文件前面有 M 标记。 输出中有两栏,左栏指明了暂存区的状态,右栏指明了工作区的状态。
忽略文件
一般我们总会有些文件无需纳入Git的管理,也不希望它们总出现在未跟踪文件列表。通常,都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。在这种情况下,我们可以创建一个名为.gitignore的文件,列出要忽略的文件的模式。
查看已暂存和未暂存的修改
如果git status命令的输出对于你来说过于简略,而你想知道具体修改了什么地方,可以用git diff命令。git diff能通过文件补丁的格式,来更加具体地显示哪些行发生了改变。
若要查看已暂存的将要添加到下次提交里的内容,可以用git diff ----staged命令。这条命令将比对已暂存文件与最后一次提交的文件差异:
请注意,git diff本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。所以,有时候你一下暂存了所有更新过的文件,运行git diff后却什么也没有,就是这个原因。
然后用 git diff --cached 查看已经暂存起来的变化( --staged 和 --cached 是同义词):
提交更新
现在的暂存区已经准备就绪,可以提交了。在此之前,请务必确认还有什么已修改或新建的文件还没有git add过,否则提交的时候不会记录这些尚未暂存的变化。这些已修改但未暂存的文件只会保留在本地磁盘。所以,每次准备提交前,先用 git status 看下,你所需要的文件是不是都已暂存起来了, 然后再运行提交命令 git commit:
$ git commit
跳过使用暂存区域
尽管使用暂存区域的方式,可以精心准备要提交的细节,但有时候这么做略显繁琐。Git提供了一个跳过使用暂存区的方式,只要在提交的时候,给git commit加上**-a选项,Git就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add步骤:
移除文件
要从Git中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。可以用git rm命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
如果只是简单地从工作目录中手工删除文件,运行git status时就会在"Changes not staged for commit"部分(也就是未暂存清单**)看到:
然后,再运行git rm已记录此次移除文件的操作:
下一次提交时,该文件就不再纳入版本管理了。 如果要删除之前修改过或已经放到暂存区的文件,则必须使用强制删除选项 -f(译注:即 force 的首字母)。 这是一种安全特性,用于防止误删尚未添加到快照的数据,这样的数据不能被 Git 恢复。
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆 .a 这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 –cached 选项:
$ git rm --cached README
移动文件
如果在 Git 中重命名了某个文件,仓库中存储的元数据并不会体现出这是一次改名操作。
要在Git中对文件改名,可以这么做:
$ git mv file_from file_to
其实,运行git mv就相当于运行了下面三条命令:
$ mv README.md README $ git rm README.md $ git add README
如此分开操作,Git也会意识到这是一次重命名,所以,不管何种方式结果都一样。。两者唯一的区别是,mv是一条命令而非三条命令,直接用git mv方便很多。不过有时候用其他工具批处理重命名的话,要记得在提交前删除旧的文件名,再添加新的文件名。
2.3Git基础–查看提交历史查看提交历史
完成这个任务,最简单而又有效的工具是git log命令。
其他最常用的选项:
①其中一个比较有用的选项是-p或-patch,它会显示每次提交所引入的差异(按补丁的格式输出);
你也可以限制显示的日志条目数量,例如:使用**-2**选项来只显示最近的两次提交:
$ git log -p -2
该选项除了显示基本信息之外,还附带了每次提交的变化。当进行代码审查,或者快速游览某个搭档的提交所带来的变化的时候,这个参数就非常有用了。
②可以为git log附带一系列的总结性选项,使用–stat选项,可以看到每次提交的简略统计信息:
如上所示,–stat选项在每次提交的下面列出,所有被修改过的文件、有多少文件被修改了以及被修改过的文件的哪些被移除或是添加了。在每次提交的最后还有一个总结。
③另一个非常有用的选项是–pretty。这个选项可以使用不同于默认格式的方式展示提交历史。
还有一些子选项供你使用。比如:oneline会将每个提交放在一行显示,在游览大量的提交时非常有用。另外,还有short,full和fuller选项,它们展示信息的格式基本一致,但是详尽程度不一:
④format,可以定制记录的显示格式。这样的输出对后期提取分析格外有用。
git log --pretty=format 常用的选项:
当oneline或format与另一个log选项–graph结合使用时尤其重要。
git log 的常用选项 :
限制输出长度:
①类似 –since 和 --until 这种按照时间作限制的选项很有用。
列出最近两周的所有提交:
$ git log --since=2.weeks
②过滤器**–s,它接受一个字符串参数**,并且只会显示那些添加或删除了该字符串的提交。假设你想找出添加或删除了对某一个特定函数的引用的提交,可以调用:
$ git log -S function_name
③ git log 选项是路径(path), 如果只关心某些文件或者目录的历史提交,可以在 git log 选项的最后指定它们的路径。 因为是放在最后位置上的选项,所以用两个短划线(–)隔开之前的选项和后面限定的路径名。
撤销操作:
有时候,我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。此时,可以运行带有–amend选项的提交命令来重新提交:
$ git commit --amend
这个命令,会将暂存区的文件提交。如果,自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。
文本编辑器启动后,可以看到之前的提交信息。编辑后保存会覆盖原来的提交信息。
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
$ git commit -m 'initial commit' $ git add forgotten_file $ git commit --amend
最终,你只会有一个提交----第二次提交将替代第一次提交的结果。
取消暂存的文件:
例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交, 但是却意外地输入 git add * 暂存了它们两个。如何只取消暂存两个中的一个呢?
使用 git reset HEAD … 来取消暂存。
撤销对文件的修改:
那如何撤消之前所做的修改呢?
$ git checkout -- CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD..." to unstage) renamed: README.md -> README
可以看到那些修改已经被撤销了。
2.5Git基础–远程仓库的使用查看远程仓库:
想查看已配置的远程仓库服务器,可以运行git remote命令。它会列出你指定的每一个远程服务器的简写。
你也可以指定选项–v,会显示需要读写远程仓库使用的Git保存的简写与其对应的URL。
$ git remote -v origin https://github.com/schacon/ticgit (fetch) origin https://github.com/schacon/ticgit (push)
如果你的远程仓库不止一个,该命令会将它们全部列出。例如,与几个协作者合作的,拥有多个远程仓库的仓库看起来像下面这样:
$ cd grit $ git remote -v bakkdoor https://github.com/bakkdoor/grit (fetch) bakkdoor https://github.com/bakkdoor/grit (push) cho45 https://github.com/cho45/grit (fetch) cho45 https://github.com/cho45/grit (push) defunkt https://github.com/defunkt/grit (fetch) defunkt https://github.com/defunkt/grit (push) koke git://github.com/koke/grit.git (fetch) koke git://github.com/koke/grit.git (push) origin git@github.com:mojombo/grit.git (fetch) origin git@github.com:mojombo/grit.git (push)
添加远程仓库:
运行 git remote add 添加一个新的远程 Git 仓库,同时指定一个方便使用的简写:
$ git remote origin $ git remote add pb https://github.com/paulboone/ticgit $ git remote -v origin https://github.com/schacon/ticgit (fetch) origin https://github.com/schacon/ticgit (push) pb https://github.com/paulboone/ticgit (fetch) pb https://github.com/paulboone/ticgit (push)
现在你可以在命令行中使用字符串 pb 来代替整个 URL。
从远程仓库中抓取与拉取:
从远程仓库中获取数据,可以执行:
$ git fetch
这个命令会访问远程仓库,从中拉取所有你还没有的数据。执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
注意git fetch命令只会将数据下载到你的本地仓库----它并不会自动合并或修改你当前的工作。当准备好时,你必须手动将其合并入你的工作。
如果,你当前的分支设置了跟踪远程分支,那么可以用git pull命令来自动抓取后合并该远程分支到当前分支。运行git pull通常会从最初克隆的服务器上,抓取数据并自动尝试合并到当前所在的分支。
推送到远程仓库:
当你想分享你的项目时,必须将其推送到上游。 这个命令很简单:git push 。 当你想要将 master 分支推送到 origin 服务器时, 那么运行这个命令就可以将你所做的备份到服务器:
$ git push origin master
只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。
查看某个远程仓库:
如果想要查看某一个远程仓库的更多信息,可以使用git remote show 命令。 如果想以一个特定的缩写名运行这个命令:
$ git remote show origin
* remote origin
Fetch URL: https://github.com/schacon/ticgit
Push URL: https://github.com/schacon/ticgit
HEAD branch: master
Remote branches:
master tracked
dev-branch tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)
它同样会列出远程仓库的URL与跟踪分支的信息。
还可以通过git remote show看到更多的信息。
$ git remote show origin
* remote origin
URL: https://github.com/my-org/complex-project
Fetch URL: https://github.com/my-org/complex-project
Push URL: https://github.com/my-org/complex-project
HEAD branch: master
Remote branches:
master tracked
dev-branch tracked
markdown-strip tracked
issue-43 new (next fetch will store in remotes/origin)
issue-45 new (next fetch will store in remotes/origin)
refs/remotes/origin/issue-11 stale (use 'git remote prune' to remove)
Local branches configured for 'git pull':
dev-branch merges with remote dev-branch
master merges with remote master
Local refs configured for 'git push':
dev-branch pushes to dev-branch (up to date)
markdown-strip pushes to markdown-strip (up to date)
master pushes to master (up to date)
这个命令列出了当你在特定的分支上执行 git push 会自动地推送到哪一个远程分支。 它也同样地列出了哪些远程分支不在你的本地,哪些远程分支已经从服务器上移除了, 还有当你执行 git pull 时哪些本地分支可以与它跟踪的远程分支自动合并。
远程仓库的重命名与移除:
你可以运行git remote rename来修改一个远程仓库的简写名。例如,想要将pb重命名为paul,可以用git remote rename这样做:
$ git remote rename pb paul $ git remote origin paul
值得注意的是这同样也会修改你所有远程跟踪的分支名字。 那些过去引用 pb/master 的现在会引用 paul/master。
如果因为一些原因想要移除一个远程仓库——你已经从服务器上搬走了或不再想使用某一个特定的镜像了, 又或者某一个贡献者不再贡献了——可以使用git remote remove或git remote rm:
一旦你使用这种方式删除了一个远程仓库,那么所有和这个远程仓库相关的远程跟踪分支以及配置信息,也会一起被删除。
打标签
像其他VCS一样你,Git可以给仓库历史中的某一个提交打上标签,以示重要。比较有代表性的是人们会使用这个功能来标记发布结点(V1.0、V2.0等等)。
列标签
在Git中列出已有的标签非常简单,只需要输入git tag(可带上可选的-l选项–list):
$ git tag v1.0 v2.0
这个命令以字母顺序列出标签,但是它们显示的顺序并不重要。
也可以按照特定的模式查找标签。例如,Git自身的源代码仓库包含标签的数量超过500个。如果只对1.8.5系列感兴趣,可以运行:
$ git tag -l "v1.8.5*" v1.8.5 v1.8.5-rc0 v1.8.5-rc1 v1.8.5-rc2 v1.8.5-rc3 v1.8.5.1 v1.8.5.2 v1.8.5.3 v1.8.5.4 v1.8.5.5
创建标签
Git支持两种标签:轻量标签(lightweight)与附注标签(annotated)。
轻量标签很像一个不会改变的分支–它只是某个特定提交的引用。
而附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证。 通常会建议创建附注标签,这样你可以拥有以上所有信息。但是如果你只是想用一个临时的标签, 或者因为某些原因不想要保存这些信息,那么也可以用轻量标签。
附注标签
在Git中创建附注标签十分简单。最简单的方式是当你在运行tag命令时指定–a选项:
$ git tag -a v1.4 -m "my version 1.4" $ git tag v0.1 v1.3 v1.4
–m选项指定了一条将会存储在标签中的信息。如果没有为附注标签指定一条信息,Git会启动编辑器要求你输入信息。
通过使用 git show 命令可以看到标签信息和与之对应的提交信息:
$ git show v1.4 tag v1.4 Tagger: Ben StraubDate: Sat May 3 20:19:12 2014 -0700 my version 1.4 commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number
输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息。
轻量标签
另一种给提交打标签的方式是使用轻量标签。轻量标签本质上是将提交校验和存储到一个文件中----没有保存任何其他信息。创建轻量标签,不需要使用–a、–s或–m选项,只需要提供标签名字:
$ git tag v1.4-lw $ git tag v0.1 v1.3 v1.4 v1.4-lw v1.5
这时,如果在标签上运行git show,你不会看到额外的标签信息。命令只会显示出提交信息:
$ git show v1.4-lw commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott ChaconDate: Mon Mar 17 21:52:11 2008 -0700 changed the version number
后期打标签
你也可以对过去的提交打标签。假设提交历史是这样的:
$ git log --pretty=oneline 15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment' a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support 0d52aaab4479697da7686c15f77a3d64d9165190 one more thing 6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment' 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function 4682c3261057305bdd616e23b64b0857d832627b added a todo file 166ae0c4d3f420721acbb115cc33848dfcc2121a started write support 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile 964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo 8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
现在,假设在v1.2时,你忘记给项目打标签,也就是在"updated rakefile"提交。你可以在之后补上标签。要在那个提交上打标签,你需要在命令的末尾指定提交的校验和(或部分校验和):
$ git tag -a v1.2 9fceb02
可以看到你已经在那次提交上打上标签了:
$ git tag v0.1 v1.2 v1.3 v1.4 v1.4-lw v1.5 $ git show v1.2 tag v1.2 Tagger: Scott ChaconDate: Mon Feb 9 15:32:16 2009 -0800 version 1.2 commit 9fceb02d0ae598e95dc970b74767f19372d61af8 Author: Magnus Chacon Date: Sun Apr 27 20:43:35 2008 -0700 updated rakefile ...
共享标签
默认情况下,git push命令并不会传送标签在远程仓库服务器上。在创建完标签后,你必须显示地推送标签到共享服务器上。这个过程就像共享远程分支一样----你可以运行 git push origin 。
$ git push origin v1.5 Counting objects: 14, done. Delta compression using up to 8 threads. Compressing objects: 100% (12/12), done. Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done. Total 14 (delta 3), reused 0 (delta 0) To git@github.com:schacon/simplegit.git * [new tag] v1.5 -> v1.5
如果想要一次性推送很多标签,也可以使用带有–tags选项的git push命令。这将会把所有不在远程仓库服务器上的标签全部传送到那里。
$ git push origin --tags Counting objects: 1, done. Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done. Total 1 (delta 0), reused 0 (delta 0) To git@github.com:schacon/simplegit.git * [new tag] v1.4 -> v1.4 * [new tag] v1.4-lw -> v1.4-lw
现在,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。
删除标签
要删除你本地仓库上的标签,可以使用命令git tag -d 。例如,可以使用以下命令删除一个轻量标签:
$ git tag -d v1.4-lw Deleted tag 'v1.4-lw' (was e7d5add)
注意上述命令并不会从任何远程仓库中移除这个标签,你必须用git push
:refs/tags/ 来更新你的远程仓库:
第一种变体是 git push :refs/tags/ :
$ git push origin :refs/tags/v1.4-lw To /git@github.com:schacon/simplegit.git - [deleted] v1.4-lw
上面这种操作的含义是,将冒号前面的控制推送到远程标签名,从而高效的删除它。
第二种更直观的删除远程标签的方式是:
$ git push origin --delete
检出标签
如果你想查看某个标签所指向的文件版本,可以使用git checkout命令,虽然,这会使你的仓库处于“分离头指针”的状态。
如果不想每次都输入完整的 Git 命令,可以通过 git config 文件来轻松地为每一个命令设置一个别名。
Git只是简单地将别名替换为对应的命令。然而,你可能想要执行外部命令,而不是一个Git子命令。如果是那样的话,可以在命令前面加入!符号。现在演示将git visual '!gitk’定义为gitk的别名:
$ git config --global alias.visual '!gitk'2.8Git基础–总结
总结:
现在,你应该能完成所有 Git 基本的本地操作了-创建或克隆一个仓库、进行更改、暂存并提交这些更改、浏览仓库从创建到现在的所有更改历史。 接下来,本书将介绍 Git 的杀手级特性:分支模型。
参考博客:
Git https://git-scm.com/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E8%8E%B7%E5%8F%96-Git-%E4%BB%93%E5%BA%93



