Git简明教程

Git介绍及基本概念

  1. 分布式版本控制系统

Git是一个分布式版本控制系统,每个本地保留远程仓库的完整副本,在本地可以进行任何版本控制操作,比如修改,提交,创建分支,合并分支,回退等。需要时才推送到远程仓库。

  1. Git文件保存

Git对每次提交,有变化的文件都会整个文件存储起来,而不是像其它版本控制系统,比如cvs,svn,perforce等存储的是文件的差异(#直接记录快照,而非差异比较)部分。但是git有package机制,适当的时候会自动运行git gc命令(也可以手动运行)对文件进行差异存储。当然为了节约空间,也会进行压缩。这种方式在分支方面会带来很大的好处,Git的分支,其实本质上仅仅是指向提交对象的可变指针。

  1. Git对数据的引用

Git中所有数据,包括提交,目录,文件等,在存储前都计算校验和,然后以校验和来引用,这个校验和相当于指针。Git用以计算校验和的机制叫做 SHA-1 散列(hash,哈希),这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串。 SHA-1 哈希看起来是这样:24b9da6552252987aa493b52f8696cd6d3b00373。

  1. Git对象

Git有4种对象,blob对象(通常是文件),树(tree)对象(通常是目录),提交(commit)对象和标签(tag)对象。当使用 git commit 进行提交操作时,Git会先计算每一个文件的校验和,在Git仓库中保存为blob对象,然后计算每一个子目录的校验和,保存为树对象,随后,Git便会创建一个提交对象,它包含提交信息(比如提交者名字,邮件,提交时间等),指向这个树对象(项目根目录)的指针和父提交对象(第一次提交无父提交对象)。如此一来,Git 就可以追踪任何对象。标签对象实际上是一个加了标签信息的提交对象,它像是一个永不移动的分支引用——永远指向同一个提交对象,只不过给这个提交对象加上一个更友好的名字罢了。

Blob对象

一个blob通常用来存储文件的内容.


image.png

你可以使用git show命令来查看一个blob对象里的内容。

Tree对象

一个tree对象有一串(bunch)指向blob对象或是其它tree对象的指针,它一般用来表示内容之间的目录层次关系。


image.png

git show命令还可以用来查看tree对象,但是git ls-tree能让你看到更多的细节。

Commit对象

"commit对象"指向一个"tree对象", 并且带有相关的描述信息.


image.png

你可以用 --pretty=raw 参数来配合git show或 git log 去查看某个提交(commit):
Tag对象


image.png

你可以用 git cat-file命令来查看。

如果我们一个小项目, 有如下的目录结构:
$ tree
.
|-- README
|-- lib
|-- inc
|-- tricks.rb
|-- mylib.rb
2 directories, 3 files
如果我们把它提交(commit)到一个Git仓库中, 在Git中它们也许看起来就如下图:


image.png

你可以看到: 每个目录都创建了 tree对象 (包括根目录), 每个文件都创建了一个对应的 blob对象 . 最后有一个 commit对象来指向根tree对象(root of trees), 这样我们就可以追踪项目每一项提交内容.

5.Git目录
这个叫'.git'的目录在项目的根目录下,存储所有历史和元信息,包括所有的对象(commits,trees,blobs,tags)。如下:

|-- HEAD # 这个git项目当前处在哪个分支里
|-- config # 项目的配置信息,git config命令会改动它
|-- description # 项目的描述信息
|-- hooks/ # 系统默认钩子脚本目录
|-- index # 索引文件
|-- logs/ # 各个refs的历史信息
|-- objects/ # Git本地仓库的所有对象 (commits, trees, blobs, tags)
|-- refs/ # 标识你项目里的每个分支指向了哪个提交(commit)。

6.工作目录
Git的 '工作目录' 存储着你现在签出(checkout)来用来编辑的文件。当你在项目的不同分支间切换时, 工作目录里的文件经常会被替换和删除。所有历史信息都保存在 'Git目录'中,工作目录只用来临时保存签出(checkout) 文件的地方, 你可以编辑工作目录的文件直到下次提交(commit)为止。

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


image.png

Git基本应用

  1. Git 配置

使用Git的第一件事就是设置你的名字和email,这些就是你在提交commit时的签名。

$ git config --global user.name "Administator"

$git config --global user.email "gitlab@niuer.tech"

执行了上面的命令后,会在你的主目录(home directory)建立一个叫 ~/.gitconfig 的文件. 内容一般像下面这样:

[user]

    name = Administator

    email = gitlab@niuer.tech

如果你想使项目里的某个值与前面的全局设置有区别(例如把私人邮箱地址改为工作邮箱);你可以在项目中使用git config 命令不带 --global 选项来设置. 这会在你项目目录下的 .git/config 文件增加一节[user]内容(如上所示).

$ git config --list ---查看当前config。

  1. 建立仓库

$ git init

$ git clone git@git.niuer.tech:root/training.git

  1. 提交到Git仓库

先修改或者创建文件file1,file2,然后添加file1,file2到暂存区(index):

$ git add file1 file2

也可以添加所有文件到暂存区:

$ git add .

如果不想提交file2,可以从暂存区移除它:

$ git rm --cached file2

(有了第一次提交之后,可以git reset file2 替代git rm --cached file2)

最后提交到git仓库:

$ git commit -m 'my first commit'

commit前可以用git status查看状态:

$ git status

  1. 回退提交

有2种方式:

$ git reset [--soft | --mixed | --hard]

--mixed

默认方式,会保留源码,只是将git commit和index信息回退到了某个提交(版本)(修改.git/HEAD的内容),如果还需要提交,先add再直接commit,或者commit -a

--soft

保留源码,只回退到commit 信息到某个提交(版本),不涉及index的回退,如果还需要提交,直接commit即可.

--hard

不保留源码, 源码和commit,index 都会回退到某个提交(版本)。这种方式比较危险,reset commit_id相当于把commit_id之后的提交删除,git log里都不会出现(当然git reflog里会有,所以也能undo reset)。

举例:

撤销到上一个版本:

$ git reset HEAD~1

撤销到任意一个版本:

$ git log ---查看提交,然后

$ git reset xxx ---恢复到版本xxx或者git reset HEAD~n ---恢复到倒数第n个提交。

当你push到远程仓库之后再reset,修改文件再commit,没问题,可是push的时候问题就来了,这个时候相当于有人在远程仓库里已经push了你上次的提交,必然会产生冲突,你就需要解决冲突才能push。

  1. 清除/废弃提交

$ git revert

git revert用一个新提交来清除/废弃某些或者某个历史提交(比如a123)所做的任何修改,历史提交a123未修改的文件不会受影响,但是如果a123之后修改了a123修改的文件,那么就需要合并了。执行revert命令时要求工作树必须是干净的。

git reset 跟git revert有时候容易搞混淆,其实这两个命令区别还是挺大的,前者回退/回滚到某个提交(版本),某个提交之后的提交都不要了;而后者是清除/废弃某些/某个提交,就是说某些/某个提交不要了,但是之后的提交还要的。

  1. [忽略文件]

在工作区根目录创建一个叫.gitignore的文件,把不需要git来管理的文件名放在里面,可以使用通配符格式。

格式规范如下:

所有空行或者以 # 开头的行都会被 Git 忽略。

可以使用标准的 glob 模式匹配。

匹配模式可以以(/)开头防止递归。

匹配模式可以以(/)结尾指定目录。

要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。

所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号()匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 使用两个星号() 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/z 或 a/b/c/z等。

  1. 分支

7.1分支介绍

假设我们有三个文件:README test.rb LICENSE第一次提交到了仓库,现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息):

image.png

我们做了2次修改后又提交了2次,这时候提交对象看起来就是下面这个样子:
image.png

分支是这样:
image.png

用命令
git checkout testing
切换分支到testing之后:
image.png

vim test.rb git commit -a -m 'made a change'

image.png

切换回master分支:
$ git checkout master
image.png

vim test.rb git commit -a -m 'made other changes'

image.png

7.2创建分支
从当前分支创建dev分支:
git branch
显示所有分支,包括远程分支:
git checkout dev
7.3合并分支
假设远程分支"origin"已经有了2个提交,如图
image.png

现在我们在这个分支做一些修改,然后生成两个提交(commit),但是与此同时,有些人也在"origin"分支上做了一些修改并且做了提交了. 这就意味着"origin"和"mywork"这两个分支各自"前进"了,它们之间"分叉"了。
image.png

正常合并:
git checkout mywork git merge origin
$ git commit -a

image.png

变基(rebase)合并:
git checkout mywork git rebase origin

image.png

$ git merge origin
image.png

解决合并中的冲突

如果你修改了某个文件,提交之前有人修改该个文件,就会有冲突,执行自动合就不会成功,git会在索引和工作树里设置一个特殊的状态, 提示你如何解决合并中出现的冲突。

有冲突(conflicts)的文件会保存在索引中,除非你解决了问题了并且更新了索引,否则执行 git commit都会失败:

$ git commit

file.txt: needs merge

如果执行 git status会显示这些文件没有合并(unmerged),这些有冲突的文件里面会添加像下面的冲突标识符:

<<<<<<< HEAD:file.txt

Hello world

=======

Goodbye

>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

你所需要的做是就是编辑解决冲突,(接着把冲突标识符删掉),再执行下面的命令:

$ git add file.txt

$ git commit 或者

$ git rebase --continue

如果有了冲突,想取消合并

$ git merge --abort 或者

$ git rebase --abort

如果dev分支不要了,可以删除:

$ git branch -d dev

删除远端分支:

$ git push origin --delete dev

查看已合并分支:

$ git branch --merged

查看未合并分支:

$ git branch --no-merged

  1. 打标签

打一个轻量标签:

$ git tag v1.0 [commit_id]

打一个附注标签(annotated):

$ git tag -a v2.0 -m "version 2.0" [commit_id]

显示标签:

$ git tag

推送标签到远程仓库:

$ git push origin v2.0

  1. 比较

比较工作目录(Working tree)和暂存区域快照(index)之间的差异,也就是修改之后还没有暂存起来的变化内容:

$ git diff

比较已经暂存起来但是还没提交的差异,也就是下一次commit时会提交到HEAD的内容:

$ git diff --cached

显示工作版本(Working tree)和HEAD(上次提交)的差异:

$ git diff HEAD

比较dev最新提交跟master最新提交之间的差异:

$ git diff master dev or git diff master..dev

比较dev从master分支分开以来的差异,就是dev与分开点的差异:

$ git diff master...dev

比较任意两个提交之间的差异

$ git diff fabca703f9682cbc6 2dabcf99b863c46b4f

比较dev最新提交跟master最新提交之间的关于文件test.txt的差异:

$ git diff master..dev test.txt

  1. 储藏

在工作区修改了文件,暂时不想提交,现在又想要切换分支,这时候就需要把修改储藏起来:

$ git stash

切换回分支过后,把储藏起来的文件取回工作区:

$ git stash pop

  1. 彻底删除文件

比如我现在的目录是这样:


image.png

做几个提交如下:

$ git add file1

$ git commit -m 'add file1'

$ git add test.tar

$ git commit -m 'add the big tar file test.tar'

$ git add file2

$ git commit -m 'add file2'

$ git rm --cached test.tar

$ git commit -m 'remove the big tar file test.tar'

$ git branch dev

$ git checkout dev

$ git add m.sh

$ git commit -m 'add m.sh'

我们看一下完整的git log以便和彻底删除文件之后的比较:

$ git log

commit 67d712fcd4c3e9b02bda773151def4421657a160
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add m.sh

commit 52ec9d47f010d7132fe3cbcc5c53dda5aed129e0
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
remove the big tar file test.tar

commit e007b16860cd55470e5c57ebccf0e2e721d81530
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add file2

commit a6f1b49c37c4c724e579088d28fce711f229c028
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add the big tar file test.tar

commit 62c6d0887e87da7d5c17aa705d933adc0a9c0507
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:09 2019 +0800
add file1

$ git checkout master

$ git log

commit 52ec9d47f010d7132fe3cbcc5c53dda5aed129e0
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
remove the big tar file test.tar

commit e007b16860cd55470e5c57ebccf0e2e721d81530
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add file2

commit a6f1b49c37c4c724e579088d28fce711f229c028
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add the big tar file test.tar

commit 62c6d0887e87da7d5c17aa705d933adc0a9c0507
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:09 2019 +0800
add file1

我们看看仓库大小:

$ git count-objects -v

count: 11

size: 221428

in-pack: 0

packs: 0

size-pack: 0

prune-packable: 0

garbage: 0

size-garbage: 0

看到没,虽然我们删除了它,其实它还是在里面的,每次提交都要对它进行SHA-1计算,每次clone都会下载它,虽然我们根本不需要它。

接下来我们要彻底删除它:

先要找到添加这个大文件的提交:

$ git log --oneline --branches -- test.tar

52ec9d4 remove the big tar file test.tar

a6f1b49 add the big tar file test.tar

这里是a6f1b49,

然后,你必须重写 a6f1b49 提交及其之后的所有提交才能从Git历史中完全移除这个文件。为什么要重写a6f1b49 之后的所有提交呢?回顾一下前面讲的git对象,每次提交都要计算各个对象的SHA-1值,既然一个文件彻底移除了,那么包含这个文件的树对象及其父树对象,提交对象及其子提交对象都要重新计算。

$ git filter-branch --index-filter 'git rm --ignore-unmatch --cached test.tar' --prune-empty -- a6f1b49 HEAD --all

Rewrite a6f1b49c37c4c724e579088d28fce711f229c028 (2/5)rm 'test.tar'

Rewrite e007b16860cd55470e5c57ebccf0e2e721d81530 (3/5)rm 'test.tar'

Rewrite 67d712fcd4c3e9b02bda773151def4421657a160 (5/5)

Ref 'refs/heads/master' was rewritten

Ref 'refs/heads/dev' was rewritten

WARNING: Ref 'refs/heads/master' is unchanged

解释下,这条命令的意思是在index(cached)里删除test.tar,然后在branch里删除test.tar。这里要注意的是符号" -- "起分隔filter-branch选项和rev-list选项的作用,所以这里的--all是指rev-list的选项--all,就是refs/ 下面的所有refs,可以理解为所有分支。

看一下log:

$ git log

commit e92badb3ba49c072fdcd234d65f49d8dd9dc04da
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add file2

commit 62c6d0887e87da7d5c17aa705d933adc0a9c0507
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:09 2019 +0800
add file1

看到没,从添加这个大文件开始之后的提交全部被改写了。

看一下dev分支:

$ git checkout dev

$ git log

commit 41316c577c29473f73aea323bbc8e539cd500aa3
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add m.sh

commit e92badb3ba49c072fdcd234d65f49d8dd9dc04da
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:20 2019 +0800
add file2

commit 62c6d0887e87da7d5c17aa705d933adc0a9c0507
Author: Administrator support@niuer.tech
Date: Wed Aug 21 15:24:09 2019 +0800
add file1

让我们看看节省了多少空间:

$ git count-objects -v

count: 16

size: 221448

in-pack: 0

packs: 0

size-pack: 0

prune-packable: 0

garbage: 0

size-garbage: 0

啊?!没有节省空间嘛……

因为还有引用指向这个大文件,你需要手动删除以前的引用:

$ rm -Rf .git/refs/original

更新本地log:

$ git reflog expire --expire=now --all

如果有远程引用,删除它们,比如:

$ git remote rm origin

再做一次垃圾回收:

$git gc --prune=now

这下大文件就彻底永久的删除了:

$ git count-objects -v

count: 0

size: 0

in-pack: 9

packs: 1

size-pack: 5

prune-packable: 0

garbage: 0

size-garbage: 0

如果你不知道某个大文件在哪里,可以通过下面的命令查找大文件的SHA-1值(第一列)和文件大小(第三列):

$ git gc

$ git verify-pack -v .git/objects/pack/pack-0b512708d645a6b5f15891de74b68fac1f6abd1b.idx | sort -k 3 -n

chain length = 1: 2 objects

.git/objects/pack/pack-0b512708d645a6b5f15891de74b68fac1f6abd1b.pack: ok

b18797efe0949a25dff3fec9b58760f3cf86a793 tree 4 14 719 1 71cdad7926263a6b82c8dcc189ab086f6e50dac1

26a46ea3ec23a452abd49d6461a0d97f26c3f3e7 blob 7 18 4364 1 f3362d9be6f76acff3a8de6fdf04b7b6a6cd8b6b

non delta: 9 objects

33ba55b0966d37bd4b396f640acdf28c7631b1c0 tree 33 44 226571881

57c286b455dc3cf998c36b18c7175b6734b19d5a tree 69 77 4382

71cdad7926263a6b82c8dcc189ab086f6e50dac1 tree 102 103 616

fa6d0e110b78152883a3d6dd28eb4f94ca32389c commit 176 122 494

7a333477d059486728ac43c41f9b5a899c6a3f26 commit 224 151 343

7e8910d778a62f8a315476840e289ef5e90b7dc8 commit 244 164 179

acbe66fd2cf05b050fe39e4a999e38fffd078dec commit 247 167 12

f3362d9be6f76acff3a8de6fdf04b7b6a6cd8b6b blob 7606 3631 733

f2595ad951ee37ffc07c6df054d93fe5b09a3c28 blob 230195200 226567422 4459

然后:

$ git rev-list --objects --all | grep f2595ad951e

f2595ad951ee37ffc07c6df054d93fe5b09a3c28 test.tar

\color{red}{警告:}文件这个操作对提交历史的修改是破坏性的,一般由管理员完成。管理员先通知所有开发人员push他们的提交,之后让他们不要再做任何操作。自己做好备份,然后才执行这个操作,最后$ git push --all origin

  1. 远程仓库

查看远程仓库配置:

$ git remote -v

添加远程仓库链接tr:

$ git remote add tr git@git.niuer.tech:root/training.git

删除远程仓库链接tr:

$ git remote rm tr

查看远程仓库链接tr:

$ git remote show tr

重命名tr为training:

$ git remote rename tr training

从远程仓库tr拉取分支dev并合并到当前分支

$ git pull tr dev

git fetch 跟git pull区别:

git pull用remote仓库更新本地仓库(merge),并更新本地分支指针比如.git/refs/heads/master和本地保存的remote分支指针比如.git/refs/remotes/origin/master;

git fetch只更新.git/FETCH_HEAD和本地保存的remote分支指针比如.git/refs/remotes/origin/master,并不跟新本地仓库。

所以,git pull=git fetch + git merge FETCH_HEAD


image.png

Git 权限控制原理
Git虽说很强大,但是Git也有一个很“严重”的问题,那就是没有权限控制功能。Git的创建是作为开源社区的代码版本管理工具而存在的,但当我们把Git引入到团队内部的开发流程中后就会发现没有权限控制的Git无法保护代码的安全。幸好还有SSH。由于Git的主流连接方式是SSH连接,因此当我们试图控制一个Git中心库权限的时候,可以通过控制SSH登录用户的权限来间接的达到目的。那如何控制SSH的用户权限呢?既然我们SSH到中心库的时候用的可能都是git用户(Linux的用户),又如何区分到底真是用户是谁呢?这里就不得不说SSH的公私钥模式,当我们将某一个用户生成的公钥添加到git用户的 ~/.ssh/authorized_keys 文件后,以git用户身份SSH连接到Git 中心库时可以免去输入密码的步骤。而Git权限控制的关键环节就在这个authorized_keys文件中。
一般典型的authorized_keys文件每一行都是一个公钥串,简单直接,但其实这个文件可以支持更丰富的SSH连接模式,请看下方的截图:

image.png

很显然,这里面除了公钥之外还包括几个其他的配置:
command:以该公钥对应的私钥登陆后执行的命令
no-xxxxx:表示不支持该模式的连接
最后才是对应的公钥。
这样配置的话,如果用户试图使用SSH默认连接方式会得到如下的返回结果:
image.png

这样可以限制用户不可以毫无顾忌的连接到Git中心库进而绕过SSH的权限控制系统。而每次Git的push/pull/clone等操作其实都是一次SSH连接从而激活command命令而执行脚本。那么剩下的就好解释了,脚本接受参数,然后去做用户权限的判断,如果用户具有权限那么直接执行SSH连接附带的命令请求,如果没有权限直接exit。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 0~3岁宝宝抵抗力低,生活自理能力还比较弱,因此, 父母 在照顾宝宝的时候要特别注意提升宝宝的身体素质。为宝宝选择...
    我家的涵涵宝宝阅读 194评论 0 0
  • 此刻正在写的文章,是加入“007不写就出局”的第四篇。好像到了时间,不写就缺少了什么,浑身上下不舒服。这代表,和七...
    逆流向上的鱼阅读 370评论 0 0
  • 以前的那个年代 书信言于心 想了就去见 可有的人呀 面不改色的说着重复的情话 乐此不疲的在各种人之间徘徊 没有感情...
    一朵儿_阅读 235评论 0 0
  • 转一个北京市里饭店阻止抽烟的事,深有同感 abcdz ()回复 ··· 楼主 2017-12-13 19:47:4...
    bigpatrician阅读 226评论 0 0
  • 无边落木萧萧下,不尽长江滚滚来。 ...
    梦晨初醒阅读 254评论 0 1