Git学习最全总结

image

程序猿工作中接触到的 Git 命令其实不是很多,常用的就那些。有时候遇到一些小功能,就上网查一下,本文就针对用到的一些小知识点做个总结。

需要先在 Github 上创建个属于你的仓库,本文仓库名以 michalel-git 为例。

地址为: git@github.com:Michael728/michael-git.git

Git 安装

Git下载地址

Windows安装时需要注意在Configuring the line ending conversions界面,选择Checkout as-is,commit as -s,避免Windows的换行符问题。如果忘记设置,可以使用如下命令后期设置:

git config --global core.autocrlf false

参考:

Git 配置

可以通过 git config -l 查看配置。

设置 Git 账号

git config --global user.name "michael728"
git config --global user.email "649168982@qq.com"

Git 配置别名

git config文件来轻松为每一个命令设置别名。例如:

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status

如果你想要执行外包命令,而不是一个 Git 子命令,可以在命令前面加 符号。

演示将git visual定义为gitk的别名:

git config --global alias.visual '!gitk'

设置命令的别名,可以提高操作效率。查看.gitconfig文件cat ~/.gitconfig

[user]
    name = xxx
    email = xxx
[i18n]
    commitencoding = utf-8
    logoutputencoding = utf-8
[core]
    quotepath = false
[filter "lfs"]
    clean = git-lfs clean -- %f
    smudge = git-lfs smudge -- %f
    process = git-lfs filter-process
    required = true
[alias]
    co = checkout
    br = branch
    c = commit
    s = status
    unstage = reset HEAD --
    last = log -1 HEAD
    lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative

[color]
    ui = true

我们可以体验一个log的别名命令设置:

lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative

这是超厉害的别名缩写命令,试试现在的 git lg 有多酷炫吧!

Git 帮助文档

授人以鱼不如授人以渔,先知道怎么通过帮助文档查看常用命令的说明吧:

git help
git help <cmd>

创建 Git 本地仓库

已有远端仓库,创建本地仓库

git clone git@github.com:Michael728/michael-git.git
cd michael-git
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master

已存在文件夹

cd micahel-git
git init
git remote add origin git@github.com:Michael728/michael-git.git
git add .
git commit
git push -u origin master

已存在 Git 仓库

cd existing_repo
git remote add origin git@github.com:Michael728/michael-git.git
git push -u origin --all # --all 表示 Push all branches,-u 选项指定了一个默认主机
git push -u origin --tags # --tags All refs under refs/tags are pushed

将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。

参考:

git clone

git clone [-b br_name] url git@github.com:Michael728/michael-git.git

克隆的时候,可以指定下载远端的分支、自定义本地仓库的名字。如果不加分支名参数,git clone 命令会默认自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(其实是仓库的默认分支)。而且,默认远程仓库设置别名为 origin

git add

这是个多功能命令:

  • 可以用它开始跟踪新文件
  • 把已跟踪的文件放到暂存区
  • 还能用于合并时把有冲突的文件标记为已解决状态,这个是在解决冲突时会用到的功能

git commit

提交的操作,当你前面采用add命令将文件添加到暂存区跟踪后,需要通过commit将暂存区的内容提交到当前分支。

git commit -m "test"

当一些已追踪的文件修改了,常常需要git add file,然后在git commit -m "xxxx",其实这两个步骤可以合二为一:

git commit -am "test"

这么写个人觉得挺好,可以有效避免有些懒人git add .的方式,将一切文件都添加到了暂存区,导致最后多余文件提交入库。

git push

语法:

git push <远程主机名> <本地分支名>:<远程分支名>

如果省略远程分支名,则表示将本地分支推送与之存在"追踪关系"的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。

当想要将master分支推送到origin服务器上:

git push origin master

将本地分支test推送到远端时可以重命名:

git push origin test:remote-test

利用该用法,还可以推送空分支到远端,实现远端分支的删除:

git push origin :remote-test
# 和如下命令等同
git push origin --delete remote-test

git rm

从 Git 中移除某个文件,就必须从已跟踪的文件清单中删除(从暂存区域移除文件),然后提交。可以使用git rm命令完成此项工作,并连带从工作目录中删除指定的文件。

当我们先把某文件从 Git 库中删除(亦即从暂存区移除),但仍然希望保留在当前工作目录中。比如当你忘记在.gitignore文件中将一些文件忽略,但是却不小心把大的日志文件添加到暂存区域时,这一做法很有用:

# --cached 将 README 文件从暂存区移除,但是工作区目录仍然保留
git rm --cached README

git checkout

切换到某个历史版本:

git checkout <commit id>

在 Git 中从当前分支创建并检出新分支的命令是:

git checkout -b new-br

这个命令实际是:

git checkout -b new-br current-br

在本地创建并切换到远端的分支:

git branch -va # 查看本地+远程分支列表
git checkout -b dev  origin/dev

还可以可以在checkout命令中使用Hash值作为起点创建分支:

git checkout -b name-of-branch <commit id>

除了有“切换”的意思,checkout还有一个撤销的作用。

举个例子,假设我们在一个分支开发一个小功能,刚写完一半,这时候需求变了,而且是大变化,之前写的代码完全用不了,好在你刚写,甚至都没有 git add 进暂存区,这个时候很简单的一个操作就直接把原文件还原:

git checkout <filename>

参考:在git中checkout历史版本

git log

git log --pretty=oneline # 检查提交日志,都在一行:<commit id> <message>

查看某人的提交:

git log --author=michael

一个常用的选项是-p,用来显示每次提交的内容差异,也可以加上-2或者-n2来仅显示最近两次提交:

git log -p -2
git log -n1 --format=format:%h # 查看当前分支最新的 commit id 缩略值

列出最近两周内的提交:

git log --since=2.weeks

git diff

git diff本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。

若要查看已暂存的将要添加到下次提交里的内容和上次提交的内容的变化,可以用git diff --staged

git branch

新建develop分支:

git branch develop

切换分支:

复习一下checkout的用法

git checkout develop

新建并切换到develop分支:

git checkout -b develop

将分支develop推送到远程仓库origin

git push origin develop

如果想给远程的分支取名为develop2,可以:

git push origin develop:develop2

不建议这么操作易混乱,还是本地分支名和远程分支名保持一致比较好。

关联本地分支和远程分支:

创建本地分支并切换到分支:git checkout -b tools-dev

创建远程分支:git push origin tools-dev

本地分支推送到远程服务器时,远程分支自动创建,推送本地分支到远程:

git push --set-upstream <remote-name> <local-branch-name>:<remote-branch-name>
  • <remote-name>:远程 Git 服务器名称,一般为origin
  • <local-branch-name>:本地分支名称
  • <remote-branch-name>:远程分支名称

一般情况下,本地分支和远程分支名称相同,所以可简化为:

git push --set-upstream <remote-name> <branch-name>

--set-upstream参数用来关联本地分支和远程分支

参考:

查看本地分支:

git branch

查看远程分支:

git branch -r

删除本地分支:

git branch -d develop
git branch -D develop(强制删除)

删除远程分支:

git push origin :remote-test
# 和如下命令等同
git push origin --delete remote-test

撤销操作

git commit --amend

如果已经提交完了(已经commit)了,发现漏掉几个文件没有添加,或者提交信息(-m)写错了,可以运行带有--amend选项的提交命令重新提交。

这个命令会将暂存区的文件提交。文本编辑器编辑后,会覆盖原来的提交信息。

取消暂存文件

git reset HEAD  <file>

撤销对文件的修改

git checkout -- <file>

你需要知道 git checkout -- [file] 是一个危险的命令,这很重要.

远程仓库的使用

查看远程仓库

git remote -v

如果想查看远程仓库更多的信息,可以使用git remote show <remote-name>命令。

远程仓库的移除与重命名

git remote rename  <old-remote-name> <new-remote-name>
git remote rename pb paul

如果因为一些原因要移除一个远程仓库,可以使用git remote rm <remote-name>

添加一个新的远程 Git 仓库,同时指定一个可以轻松引用的简写:

git remote add <remote-name> <url>

这里的remote-name常常取名为origin。所以,常见的origin其实是一个你 Git 仓库跟踪的远程仓库的简写。

拉取远端仓库有但你本地没有的信息:

git fetch <remote-name>

如果你使用clone命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以origin为缩写。

Tag

列出标签

git tag # 列出所有标签
git tag -l 'v1.8*' # 列出以 v1.8 开头的所有标签

创建标签

Git使用两种主要类型的标签:

  • 附注(annotated)标签
  • 轻量(ightweight)标签

前者会包括一些注释信息,来进一步解释这个 tag 的作用,而后者就仅仅只是一个 tag 的名字

附注标签

git tag -a v1.4 -m 'my version 1.4'

通过git show <tag-name>命令可以看到标签信息

轻量标签

git tag v1.4

没用-a-m的参数,只需要提供标签名字

删除标签

git tag -d <tagname>

补打标签

假设忘记给项目打标签,可以在之后加上:

基于某历史节点的commit id补打Tag

git tag -a v1.2 <commit id>

共享标签

默认情况下,git push命令并不会传送标签到远程服务器上。在创建完标签后你必须显示地推送标签到共享服务器上。这个过程就像共享远程分支一样,可以运行git push origin [tagname]

如果想要一次性推送很多标签,也可以使用--tags选项的git push

git push origin --tags

检出标签

git checkout -b <new-br> <tagname>

参考:

使用场景

新特性开发,创建临时分支,再合并到主干

#创建特性分支
git checkout -b featureA
……
#提交之前,先checkout到master分支,更新一下
git co master
git pull
#切到特性分支,在本地与最新的master分支合并
git co featureA
git rebase -i master

参考:

本地仓库关联远程仓库:

已有本地仓库,需要关联远端仓库,分两步:
第一步,在Github上新建一个仓库test;
第二步:将本地仓库与Github上的test项目进行关联,切换到本地仓库目录:
git remote add origin git@github.com:xx/test.git
什么意思?远程仓库的地址为:git@github.com:xx/test.git,而origin是给这项目的远程仓库起的名字,是的,名字你可以随便取,只不过大家公认的只有一个远程仓库时名字就是origin,为什么要给远程仓库取名字?因为我们可能一个项目有多个远程仓库,比如,Github一个,比如公司一个,这样的话,提交的时候可以提交到不同的远程仓库就需要指定不同的仓库名字了。

查看我们当前项目有哪些远程仓库可以执行如下命令:

git remote -v

接下来,本地的仓库可以向远程仓库进行代码提交了:

git push origin master

在代码提交之前,西安设置自己的用户名和邮箱,这些信息会出现在所有的commit记录里:

git config --global user.name "xxx"
git config --global user.email "xx@mail.com"

二分查找,傻瓜式定位bug

场景:定位Bug,当前版本有Bug,上个版本没有,两个版本之前有上千次commit
二分查找,N个patch只需要测试log2N次(8k个path仅需测试13次)
可以实现测试自动化,自动查找问题patch
git bisect:自动定位,不必找原作者

同时负责多个Bug的修改

  • 更新远程仓库代码
git fetch
  • 以origin/master为基础创建分支
git checkout -b fix/bug23 origin/master
  • 修改完
git commit
  • 推送前更新一下代码,看看别人是否有修改
git fetch
  • 有修改的话rebase一下
git rebase origin/master
  • 生成patch
git format-patch origin/master
  • 发送patch或者也可以使用request pull
send email OR Request pull
[maintainer接收后]
  • 分支使用已经完成,可以删除了
git branch -D fix/bug23

想知道某行代码谁修改的

阅读代码时,想知道某行代码是谁修改的?

  • 找到对应commit id
git blame src/xxx.c
  • 查看具体提交的内容
git show <commit id>

远端仓库会退到历史版本

  • 查找commit id
    通过git log查找想要会退到的历史版本的commit id
  • 本地执行回退
git reset --hard [commit id]
  • 强制推送
git push -f

参考:git 远程仓库版本的回退以及git reset 几种常用方式记录

突然插入bugifx,回退工作目录

git stash保存所有工作内容

cherry-pick

将某一提交点的修改拿到当前分支上:
git cherry-pick 哈希值

基本的团队协作流程

多人协作下的分支管理规范很重要,就跟代码规范一样重要。

以下就跟大家推荐一种我们内部在使用的一种分支管理流程 Git Flow。

Gti Flow
Git Flow 是一种比较成熟的分支管理流程,我们先看一张图能清晰的描述他整个的工作流程:

大部分情况下都会拥有两个分支 master 和 develop,他们的职责分别是:

  • master:永远处在即将发布(production-ready)状态
  • develop:最新的开发状态

确切的说 master、develop 分支大部分情况下都会保持一致,只有在上线前的测试阶段
develop 比 master 的代码要多,一旦测试没问题,准备发布了,这时候会将 develop 合并到
master 上。

但是,我们发布之后又会进行下一版本的功能开发,开发中间可能又会遇到需要紧急修复 bug
,一个功能开发完成之后突然需求变动了等情况,所以 Git Flow 除了以上 master 和 develop
两个主要分支以外,还提出了以下三个辅助分支:

  • feature: 开发新功能的分支, 基于 develop, 完成后 merge 回 develop
  • release: 准备要发布版本的分支, 用来修复 bug,基于 develop,完成后 merge 回develop 和 master
  • hotfix: 修复 master 上的问题, 等不及 release 版本就必须马上上线. 基于 master, 完成后merge 回 master 和 develop

什么意思呢?
举个例子,假设我们已经有 master 和 develop 两个分支了,这个时候我们准备做一个功能A,第一步我们要做的,就是基于 develop 分支新建个分支:

git branch feature/A

看到了吧,其实就是一个规范,规定了所有开发的功能分支都以 feature 为前缀。但是这个时候做着做着发现线上有一个紧急的 bug 需要修复,那赶紧停下手头的工作,立刻切换到 master 分支,然后再此基础上新建一个分支:

git branch hotfix/B

代表新建了一个紧急修复分支,修复完成之后直接合并到 develop 和 master ,然后发布。然后再切回我们的 feature/A 分支继续着我们的开发,如果开发完了,那么合并回 develop 分支,然后在 develop 分支属于测试环境,跟后端对接并且测试的差不多了,感觉可以发布到正式环境了,这个时候再新建一个 release 分支:

git branch release/1.0

这个时候所有的 api、数据等都是正式环境,然后在这个分支上进行最后的测试,发现 bug 直接进行修改,直到测试 ok 达到了发布的标准,最后把该分支合并到 develop 和 master 然后进行发布。

以上就是 Git Flow 的概念与大概流程,看起来很复杂,但是对于人数比较多的团队协作现实开发中确实会遇到这么复杂的情况,是目前很流行的一套分支管理流程,但是有人会问每次都要各种操作,合并来合并去,有点麻烦,哈哈,这点 Git Flow 早就想到了,为此还专门推出了一个 Git Flow 的工具,并且是开源的:

GitHub 开源地址: https://github.com/nvie/gitflow

简单点来说,就是这个工具帮我们省下了很多步骤,比如我们当前处于 master 分支,如果想要开发一个新的功能,第一步切换到 develop 分支,第二步新建一个以 feature 开头的分支名,有了 Git Flow 直接如下操作完成了:

git flow feature start A

这个分支完成之后,需要合并到 develop 分支,然而直接进行如下操作就行:

git flow feature finish A

如果是 hotfix 或者 release 分支甚至会自动帮你合并到 develop、master 两个分支。

想必大家已经了解了这个工具的具体作用,具体安装与用法我就不多提了,感兴趣的可以看
我下我之前写过的一篇博客: http://stormzhang.com/git/2014/01/29/git-flow/

详细演示下怎么给一个项目发起 Pull Request(PR):

  • 第一步,找到你想发起 PR 的项目,点击右上角的 Fork 按钮,然后该项目就出现在了你自己账号的 Repository 里。
  • 第二步,把fork的项目 clone 到本地,然后修改的 bug 也好,想要新增的功能也好,总之把自己做的代码改动开发完,接着,把自己做的代码改动 push 到 你自己的 GitHub 上去。
  • 第三步,点击你 Fork 过来的项目主页的 Pull requests 页面,点击右上角的New pull request。
    页面自动会比较该项目与原有项目的不同之处,最顶部声明了是源仓库的分支与你fork过来的分支的对比。同样的我写好标题和描述,然后我们点击中间的 Create pull request 按钮,至此我们就成功给该项目提交了一个 PR。
    然后就等着项目原作者 review 你的代码,并且决定会不会接受你的 PR,如果接受,那么恭喜你,你已经是该项目的贡献者之一了。

Git FAQ

git merge和git rebase的区别

git checkout master
git merge featureA

其实 rebase 命令也是合并的意思,上面的需求我们一样可以如下操作:

git checkout master
git rebase featureA

rebase 跟 merge 的区别你们可以理解成有两个书架,你需要把两个书架的书整理到一起去,第一种做法是 merge ,比较粗鲁暴力,就直接腾出一块地方把另一个书架的书全部放进去,虽然暴力,但是这种做法你可以知道哪些书是来自另一个书架的;第二种做法就是rebase ,他会把两个书架的书先进行比较,按照购书的时间来给他重新排序,然后重新放置好,这样做的好处就是合并之后的书架看起来很有逻辑,但是你很难清晰的知道哪些书来自哪个书架的。

只能说各有好处的,不同的团队根据不同的需要以及不同的习惯来选择就好。

rebase 和 merge的另一个区别是rebase 的冲突是一个一个解决,如果有十个冲突,先解决第一个,然后用命令

git add -u
git rebase --continue

继续后才会出现第二个冲突,直到所有冲突解决完,而merge 是所有的冲突都会显示出来。
另外如果rebase过程中,你想中途退出,恢复rebase前的代码则可以用命令

git rebase --abort

关于git rebase还有很多知识点:

git branch -r与git branch -a的区别?

  • git branch -r只显示远端分支,
  • git branch -a 显示本地分支和远程分支

发现好用的开源项目-Github

GitHub 其中一个最重要的作用就是发现全世界最优秀的开源项目,你没事的时候刷刷微博、知乎,人家没事的时候刷刷 GitHub ,看看最近有哪些流行的项目,久而久之,这差距就越来越大,那么如何发现优秀的开源项目呢?

  1. 关注一些活跃的大牛
  2. Explore菜单下的Trending,看到最近的一些热门开源项目,很多人主动获取开源项目的最好的途径,可以选择“当天热门”,“一周之内热门”和“一月之内热门”来查看,并且,可以分语言来查看。
  3. Search,按照Most Stars来筛选。
    除此之外,GitHub 的 Search 还有一些小技巧,比如你想搜索的结果中 star 数大于1000的,那么可以这样搜索:
    android http stars:>1000

有些人如果习惯用 Google 进行搜索,那么想搜索 GitHub 上的结果,不妨前面加 GitHub 关键字就ok了,比如我在 google 里输入 GitHub android http ,每个关键字用空格隔开。

福利大放送

GitHub 上影响力很大,同时又对你们很有用的项目:

  • free-programming-books: 这个项目整理了所有跟编程相关的免费书籍,而且全球多国语言版的都有,中文版的在这里 free-programming-books-zh
  • ob-my-zsh: 俗话说,不会用 shell 的程序员不是真正的程序员,所以建议每个程序员都懂点 shell,有用 不说,装逼利器啊!而 oh-my-zsh 毫无疑问就是目前最流行,最酷炫的 shell,不多说了,懂 得自然懂,不懂的以后你们会懂的!
  • awesome: GitHub 上有各种 awesome 系列,简单来说就是这个系列搜罗整理了 GitHub 上各领域的资源大汇总,比如有 awesome-android, awesome-ios, awesome-java, awesome-python 等等等,就不截图了,你们自行去感受。
  • github-cheat-sheet: GitHub 的使用有各种技巧,只不过基本的就够我们用了,但是如果你对 GitHub 超级感兴趣,想更多的了解 GitHub 的使用技巧,那么这个项目就刚好是你需要的,每个 GitHub 粉都应该知道这个项目。
  • LearningNotes: https://github.com/francistao/LearningNotes
    这是一份非常详细的面试资料,涉及 Android、Java、设计模式、算法等等等,你能想到的,你不能想到的基本都包含了,可以说是适应于任何准备面试的 Android 开发者,看完这个之后别说你还不知道怎么面试!

GitHub 上优秀开源项目真的是一大堆,就不一一推荐了,授人以鱼不如授人以渔,请大家自行主动发掘自己需要的开源项目吧,不管是应用在实际项目上,还是对源码的学习,都是提升自己工作效率与技能的很重要的一个渠道,总有一天,你会突然意识到,原来不知不觉你已经走了这么远!

参考:

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

推荐阅读更多精彩内容