Git学习总结

Git Community Book

 

Git介绍

Git对象模型

对象名是SHA值

 

每个对象(object) 包括三个部分:类型,大小和内容。大小就是指内容的大小,内容取决于对象的类型,有四种类型的对象:"blob"、"tree"、 "commit" 和"tag"。

 

•“blob”用来存储文件数据,通常是一个文件。

·•“tree”有点像一个目录,它管理一些“tree”或是“blob”(就像文件和子目录)

• 一个“commit”只指向一个"tree",它用来标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交(commits)的指针等等。

• 一个“tag”是来标记某一个提交(commit) 的方法。

几乎所有的Git功能都是使用这四个简单的对象类型来完成的。它就像是在你本机的文件系统之上构建一个小的文件系统。

与SVN的区别

SVN是使用“增量文件系统”存储每次提交之间的差异。Git是每次提交的文件的全部内容都会记录下来。

Git目录与工作目录

$>tree -L 1

|-- HEAD # 这个git项目当前处在哪个分支里

|-- config # 项目的配置信息,git config命令会改动它

|-- description # 项目的描述信息

|-- hooks/ # 系统默认钩子脚本目录

|-- index # 索引文件

|-- logs/ # 各个refs的历史信息

|-- objects/ # Git本地仓库的所有对象 (commits, trees, blobs, tags)

`-- refs/ # 标识你项目里的每个分支指向了哪个提交(commit)。

 

工作目录只用来临时保存签出(checkout) 文件的地方,你可以编辑工作目录的文件直到下次提交(commit)为止

 

Git索引是一个在你的工作目录和项目仓库间的暂存区(staging area). 有了它, 你可以把许多内容的修改一起提交(commit). 如果你创建了一个提交(commit), 那么提交的是当前索引(index)里的内容, 而不是工作目录中的内容.

 

查看索引:git status 命令

第一步

Git安装与初始化

$ git config --global user.name "Scott Chacon"

$ git config --global user.email "schacon@gmail.com"

基本用法

获得一个Git仓库

Clone一个仓库

git clone git://git.kernel.org/pub/scm/git/git.git

git clone http://www.kernel.org/pub/scm/git/git.git

 

初始化一个新的仓库

$ tar xzf project.tar.gz

$ cd project

$ git init

正常的工作流程

你可以使用 git diff 命令再加上 --cached 参数 ,看看哪些文件将被提交(commit)。

$ git diff --cached

(如果没有--cached参数,git diff 会显示当前你所有已做的但没有加入到索引里的修改.) 你也可以用git status命令来获得当前项目的一个状况

 

除了用git add 命令,还可以用

$ git commit -a

这会自动把所有内容被修改的文件(不包括新创建的文件)都添加到索引中,并且同时把它们提交。

分支与合并基础

创建一个新的叫”experimental”的分支:

$ git branch experimental

得到当前仓库中存在的所有分支列表:

$ git branch

切换到”experimental”分支:

$ git checkout experimental

通过下面的命令来合并“experimental”和“master”两个分支:

$ git merge experimental

如有有冲突,输入下面的命令就可以查看当前有哪些文件产生了冲突:

$ git diff

当你编辑了有冲突的文件,解决了冲突后就可以提交了:

$ git commit -a

提交(commit)了合并的内容后就可查看一下:

$ gitk

删除掉“experimental”分支了:

$ git branch -d experimental

git branch -d只能删除那些已经被当前分支的合并的分支. 如果你要强制删除某个分支的话就用git branch –D

合并分支,把branchname分支合并到当前分支:

$ git merge branchname

 

撒销一个合并

如果你觉得你合并后的状态是一团乱麻,想把当前的修改都放弃,你可以用下面的命令回到合并之前的状态:

$ git reset --hard HEAD

或者你已经把合并后的代码提交,但还是想把它们撒销:

$ git reset --hard ORIG_HEAD

查看历史-GIT日志

git log命令可以显示所有的提交(commit)

$ git log v2.5.. # commits since (not reachable from) v2.5

$ git log test..master # commits reachable from master but not test

$ git log master..test # commits reachable from test but not master

$ git log master...test # commits reachable from either test or

# master, but not both

$ git log --since="2 weeks ago" # commits from the last 2 weeks

$ git log Makefile # commits that modify Makefile

$ git log fs/ # commits that modify any file under fs/

$ git log -S'foo()' # commits that add or remove any file data

# matching the string 'foo()'

$ git log --no-merges # dont show merge commits

当然你也可以组合上面的命令选项;下面的命令就是找出所有从"v2.5“开 始在fs目录下的所有Makefile的修改

$ git log v2.5.. Makefile fs/

日志统计

$ git log –stat

格式化日志

$ git log --pretty=oneline

$ git log --pretty=short

可以用'--graph'选项来可视化你的提交图(commit graph),就像下面这样:

$ git log --pretty=format:'%h : %s' --graph

比较提交-GIT DIFF

你可以用 git diff 来比较项目中任意两个版本的差异。

$ git diff master..test

上面这条命令只显示两个分支间的差异,如果你想找出‘master’,‘test’的共有父分支和'test'分支之间的差异,你用3个‘.'来取代前面的两个'.' 。

$ git diff master...test

 

通常用git diff来找你当前工作目录和上次提交与本地索引间的差异。

$ git diff

上面的命令会显示在当前的工作目录里的,没有 staged(添加到索引中),且在下次提交时不会被提交的修改。

如果你要看在下次提交时要提交的内容(staged,添加到索引中),你可以运行:

$ git diff --cached

上面的命令会显示你当前的索引和上次提交间的差异;这些内容在不带"-a"参数运行 "git commit"命令时就会被提交。

 

$ git diff HEAD

上面这条命令会显示你工作目录与上次提交时之间的所有差别,这条命令所显示的 内容都会在执行"git commit -a"命令时被提交。

 

更多的比较选项

查看当前的工作目录与另外一个分支的差别,你可以用下面的命令执行:

$ git diff test

这会显示你当前工作目录与另外一个叫'test'分支的差别。你也以加上路径限定符,来只比较某一个文件或目录。

$ git diff HEAD -- ./lib

上面这条命令会显示你当前工作目录下的lib目录与上次提交之间的差别(或者更准确的说是在当前分支)。

如果不是查看每个文件的详细差别,而是统计一下有哪些文件被改动,有多少行被改动,就可以使用‘--stat' 参数。

$>git diff --stat

分布式的工作流程

git pull命令执行两个操作: 它从远程分支(remote branch)抓取修改的内容,然后把它合并进当前的分支。

公共Git仓库

见《Git Community Book》

将修改推送到一个公共仓库

用你本地的"master" 分支去更新远程的"master"分支,执行下面的命令:

$ git push ssh://yourserver.com/~you/proj.git master:master

or just

或是:

$ git push ssh://yourserver.com/~you/proj.git master

Git标签

轻量级标签

我们可以用 git tag不带任何参数创建一个标签(tag)指定某个提交(commit):

$ git tag stable-1 1b2e1d63ff

前面这样创建的是一个"轻量级标签",这种分支通常是从来不移动的。

如果你想为一个标签(tag)添加注释,或是为它添加一个签名(sign it cryptographically), 那么我们就需要创建一个"标签对象"

标签对象

如果有 "-a", "-s" 或是 "-u " 中间的一个命令参数被指定,那么就会创建 一个标签对象,并且需要一个标签消息(tagmessage). 如果没有"-m " 或是 "-F " 这些参数,那么就会启动一个编辑器来让用户输入标签消息(tag message).

签名的标签

在你的 .git/config 或 ~.gitconfig里配好key.

下面是示例:

[user]

signingkey = <gpg-key-id>

你也可以用命令行来配置:

$ git config (--global) user.signingkey <gpg-key-id>

现在你可以直接用"-s" 参数来创“签名的标签”。

$ git tag -s stable-1 1b2e1d63ff

如果没有在配置文件中配GPG key,你可以用"-u“ 参数直接指定。

$ git tag -u <gpg-key-id> stable-1 1b2e1d63ff

中级技能

忽略某些文件

你可以在你的顶层工作目录中添加一个叫".gitignore"的文件,来告诉Git系统要忽略掉哪些文件,下面是文件内容的示例:

# 以'#' 开始的行,被视为注释.

# 忽略掉所有文件名是 foo.txt 的文件.

foo.txt

# 忽略所有生成的 html 文件,

*.html

# foo.html是手工维护的,所以例外.

!foo.html

# 忽略所有.o 和 .a文件.

*.[oa]

你可以点这里gitignore查看一下详细的语法解释. 你也可以把".gitignore" 这个文件放到工作树(working tree)里的其它目录中,这就会在它和它的子目录起忽略(ignore) 指定文件的作用。.gitignor文件同样可以像其它文件一样加到项目仓库里(直接用 git add .gitignore 和 git commit等命令), 这样项目里的其它开发者也能共享同一套忽略文件规则。

 

如果你想忽略规则只对特定的仓库起作用,你可以把这些忽略规则写到你的仓库下 .git/info/exclude文件中,或是写在Git配置变量core.excludesfile中指定的文件里。有些Git命令也可在命令行参数中指定忽略规则,你可以在这里:gitignore查看详细的用法。

REBASE

假设你现在基于远程分支"origin",创建一个叫"mywork"的分支。

$ git checkout -b mywork origin

现在我们在这个分支做一些修改,然后生成两个提交(commit).

但是与此同时,有些人也在"origin"分支上做了一些修改并且做了提交了。这就意味着"origin"和"mywork"这两个分支各自"前进"了,它们之间"分叉"了。

在这里,你可以用"pull"命令把"origin"分支上的修改拉下来并且和你的修改合并; 结果看起来就像一个新的"合并的提交"(merge commit):

但是,如果你想让"mywork"分支历史看起来像没有经过任何合并一样,你也许可以用git rebase:

$ git checkout mywork

$ git rebase origin

这些命令会把你的"mywork"分支里的每个提交(commit)取消掉,并且把它们临时保存为补丁(patch)(这些补丁放到".git/rebase"目录中),然后把"mywork"分支更新 到最新的"origin"分支,最后把保存的这些补丁应用到"mywork"分支上。

当'mywork'分支更新之后,它会指向这些新创建的提交(commit),而那些老的提交会被丢弃。

若出现冲突,解决后:

$ git rebase --continue

在任何时候,你可以用--abort参数来终rebase的行动,并且"mywork" 分支会回到rebase始前的状
$ git rebase --abort

储藏

当你正在做一项复杂的工作时, 发现了一个和当前工作不相关但是又很讨厌的bug. 你这时想先修复bug再做手头的工作, 那么就可以用 git stash 来保存当前的工作状态, 等你修复完bug后,执行'反储藏'(unstash)操作就可以回到之前的工作里。

$ git stash "work in progress for foo feature"

上面这条命令会保存你的本地修改到储藏(stash)中, 然后将你的工作目录和索引里的内容全部重置, 回到你当前所在分支的上次提交时的状态.好了, 你现在就可以开始你的修复工作了.

... edit and test ...

$ git commit -a -m "blorpl: typofix"

当你修复完bug后, 你可以用git stash apply来回复到以前的工作状态.

$ git stash apply

储藏队列

你也可多次使用'git stash'命令,每执行一次就会把针对当前修改的‘储藏’(stash)添加到储藏队列中. 用'git stash list'命令可以查看你保存的'储藏'(stashes):

$>git stash list

GIT树名

不用40个字节长的SHA串来表示一个提交(commit)或是其它git对象

Sha短名

分支,Remote或标签

你可以使用分支,remote或标签名来代替SHA串名, 它们只是指向某个对象的指针

日期标识符

Git的引用日志(Ref Log)可以让你做一些‘相对'查询操作:

master@{yesterday}

master@{1 month ago}

上面的第一条命令是:'master分支的昨天状态(head)的缩写‘. 注意: 即使在两个有相同master分支指向的仓库上执行这条命令, 但是如果这个两个仓库在不同机器上,那么执行结果也很可能会不一样

顺序标识符

这种格式用来表达某点前面的第N个提交(ref).

master@{5}

上面的表达式代表着master前面的第5个提交(ref).

多个父对象

这能告诉你某个提交的第N个直接父提交(parent). 这种格式在合并提交(merge commits)时特别有用, 这样就可以使提交对象(commit object)有多于一个直接父对象(direct parent).

译者注:假设master是由a和b两个分支合并的,那么 master^1是指分支a, master^2就是指分支b.

master^2

波浪号

波浪号用来标识一个提交对象(commit object)的第N级嫡(祖)父对象(Nth grandparent). 例如:

master~2

就代表master所指向的提交对象的第一个父对象的第一个父对象(译者:你可以理解成是嫡系爷爷:)). 它和下面的这个表达式是等价的:

master^^

你也可以把这些‘标识符'(spec)叠加起来,下面这个3个表达式都是指向同一个提交(commit):

master^^^^^^

master~3^~2

master~6

树对象指针

如果大家对第一章Git对象模型还有印象的话, 就记得提交对象(commit object)是指向一个树对象(tree object)的. 假如你要得到一个提交对象(commit object)指向的树对象(tree object)的sha串名, 你就可以在‘树名'的后面加上'{tree}'来得到它:

master^{tree}

二进制标识符

如果你要某个二次制对象(blob)的sha串名,你可以在'树名'(treeish)后添加二次制对象(blob)对应的文件路径来得到它.

master:/path/to/file

区间

最后,你可以用".."来指两个提交(commit)之间的区间。下面的命令会给出你在"7b593b5" 和"51bea1"之间除了"7b593b5外"的所有提交(commit)(注意:51bea1是最近的提交).

7b593b5..51bea1

这会包括所有从7b593b开始的提交(commit). 译者注:相当于7b593b..HEAD

7b593b..

追踪分支

在Git中'追踪分支'是用与联系本地分支和远程分支的。如果你在'追踪分支'(Tracking Branches)上执行推送(push)或拉取(pull)时,它会自动推送(push)或拉取(pull)到关联的远程分支上。

 

如果你经常要从远程仓库里拉取(pull)分支到本地,并且不想很麻烦的使用"git pull "这种格式; 那么就应当使用'追踪分支'(Tracking Branches)。

 

‘git clone‘命令会自动在本地建立一个'master'分支,它是'origin/master'的‘追踪分支’. 而'origin/master'就是被克隆(clone)仓库的'master'分支

你可以在使用'git branch'命令时加上'--track'参数, 来手动创建一个'追踪分支'.

git branch --track experimental origin/experimental

当你运行下命令时:

$ git pull experimental

它会自动从'origin'抓取(fetch)内容,再把远程的'origin/experimental'分支合并进(merge)本地的'experimental'分支.

当要把修改推送(push)到origin时, 它会将你本地的'experimental'分支中的修改推送到origin的'experimental'分支里,而无需指定它(origin)

使用GIT GREP进行搜索

用git grep 命令查找Git库里面的某段文字是很方便的. 当然, 你也可以用unix下的'grep'命令进行搜索, 但是'git grep'命令能让你不用签出(checkout)历史文件, 就能查找它们.

例如, 你要看 git.git 这个仓库里每个使用'xmmap'函数的地方, 你可以运行下面的命令:

$ git grep xmmap

如果你要显示行号, 你可以添加'-n'选项:

$>git grep -n xmmap

如果我们想只显示文件名, 我们可以使用'--name-onley'选项:

$>git grep --name-only xmmap

我们可以用'-c'选项,可以查看每个文件里有多少行匹配内容(line matches):

$>git grep -c xmmap

现在, 如果我们要查找git仓库里某个特定版本里的内容, 我们可以像下面一样在命令行末尾加上标签名(tag reference):

$ git grep xmmap v1.5.0

我们也可以组合一些搜索条件, 下面的命令就是查找我们在仓库的哪个地方定义了'SORT_DIRENT'.

$ git grep -e '#define' --and -e SORT_DIRENT

builtin-fsck.c:#define SORT_DIRENT 0

builtin-fsck.c:#define SORT_DIRENT 1

我不但可以进行“与"(both)条件搜索操作,也可以进行"或"(either)条件搜索操作.

$ git grep --all-match -e '#define' -e SORT_DIRENT

我们也可以查找出符合一个条件(term)且符合两个条件(terms)之一的文件行。例如我们要找出名字中含有'PATH'或是'MAX'的常量定义:

$ git grep -e '#define' --and \( -e PATH -e MAX \)

GIT的撤消操作-重置,签出 和 撤消

修复未提交文件中的错误(重置)

如果你现在的工作目录(work tree)里搞的一团乱麻, 但是你现在还没有把它们提交; 你可以通过下面的命令, 让工作目录回到上次提交时的状态(last committed state):

$ git reset --hard HEAD

这条件命令会把你工作目录中所有未提交的内容清空(当然这不包括未置于版控制下的文件 untracked files). 从另一种角度来说, 这会让"git diff" 和"git diff --cached"命令的显示法都变为空.

如果你只是要恢复一个文件,如"hello.rb", 你就要使用 git checkout

$ git checkout -- hello.rb

修复已提交文件中的错误

创建新提交来修复错误:

$ git revert HEAD

下面这条命令就是撤消“上上次”(next-to-last)的提交:

$ git revert HEAD^

修改提交来修复错误:

如果你刚刚做了某个提交(commit), 但是你又想马上修改这个提交; git commit 现在支持一个叫--amend的参数,它能让你修改刚才的这个提交(HEAD commit). 这项机制能让你在代码发布前,添加一些新的文件或是修改你的提交注释(commit message).

如果你在老提交(older commit)里发现一个错误, 但是现在还没有发布到代码服务器上。你可以使用 git rebase命令的交互模式, "git rebase -i"会提示你在编辑中做相关的修改。这样其实就是让你在rebase的过程来修改提交

维护GIT

在大的仓库中, git靠压缩历史信息来节约磁盘和内存空间:(比较耗时)

$ git gc

保持可靠性: git fsck 运行一些仓库的一致性检查, 如果有任何问题就会报告. 这项操作也有点耗时, 通常报的警告就是“悬空对象"(dangling objects).

$ git fsck

 

建立一个公共仓库

假设你个人的仓库在目录 ~/proj. 我们先克隆一个新的“裸仓库“,并且创建一个标志文件告诉git-daemon这是个公共仓库.

$ git clone --bare ~/proj proj.git

$ touch proj.git/git-daemon-export-ok

上面的命令创建了一个proj.git目录, 这个目录里有一个“裸git仓库" -- 即只有'.git'目录里的内容,没有任何签出(checkedout)的文件.

 

高级技能

创建新的空分支

在偶尔的情况下,你可能会想要保留那些与你的代码没有共同祖先的分支。例如在这些分支上保留生成的文档或者其他一些东西。如果你需要创建一个不使用当前代码库作为父提交的分支,你可以用如下的方法创建一个空分支:

git symbolic-ref HEAD refs/heads/newbranch

rm .git/index

git clean -fdx

<do work>

git add your files

git commit -m 'Initial commit'

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值