Git/gcc/MPP

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理git任何或小或大的项目。 

Git 是 Linus Torvalds为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件

Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持

特点:

1、Git 是分布式的:同一个Git仓库,可以分布到不同的机器上。首先找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。可以自己搭建这台服务器,也可以使用GitHub网站。

2、Git 把内容按元数据方式存储

3、Git 没有一个全局的版本号

4、版本控制:可以解决多人同时开发的代码问题,也可以解决找回历史代码的问题

5、Git 的内容完整性优:Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏


git基本理论


设置用户信息: 每次Git提交都会使用该信息(如果去掉 --global 参数则只对当前仓库有效)

$ git config --global user.name "tianlong.li"

$ git config --global user.email "tianlong.li@esgyn.cn"

此命令在最上层目录生成一个.gitconfig隐藏文件,内容为:

查看git配置:git config -l

四个区域:

Remote:远程仓库,托管代码的服务器,可以简单的认为是你项目组中的一台电脑用于远程数据交换

Repository:本地仓库,就是安全存放数据的位置,这里面有你提交到所有版本的数据。其中HEAD指向最新放入仓库的版本

Index / Stage:暂存区,用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息

Workspace:工作区,就是你平时存放项目代码的地方

本地的三个区域确切的说应该是git仓库中HEAD指向的版本

Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间。

WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间。

.git:存放Git管理信息的目录,初始化仓库的时候自动创建。

Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区。

Local Repo:本地仓库,一个存放在本地的版本库;HEAD会只是当前的开发分支(branch)。

Stash:隐藏,是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态。

git工作流程:

1、在工作目录中添加、修改文件;

2、将需要进行版本管理的文件放入暂存区域;

3、将暂存区域的文件提交到git仓库。

因此,git管理的文件有三种状态:已修改(modified),已暂存(staged),已提交(committed)


Git项目搭建


日常使用只要记住下图6个命令:

1、创建版本库/仓库初始化:git init

在当前目录新建一个Git代码库

执行后可以看到,在项目目录多出了一个.git目录,关于版本的所有信息都在这个目录里面。

2、克隆远程仓库

首先确保本机公钥已经在仓库里面保存:

(1)使用如下命令生成ssh密钥

ssh-keygen  ( -t rsa -C "邮箱地址")

(2)进入主目录下的.ssh文件件,下面有两个文件。

公钥为id_rsa.pub,私钥为id_rsa,查看公钥内容,复制此内容至仓库ssh keys

(3)进入github,点击账户头像后的下拉三角,选择'settings',如果某台机器需要与github上的仓库交互,那么就要把这台机器的ssh公钥添加到这个github账户上

(4)点击'SSH and GPG  keys',添加ssh公钥:

接下来进行远程仓库的克隆:

git clone [url]

[git submodule]

1.背景

面对比较复杂的项目,我们有可能会将代码根据功能拆解成不同的子模块。主项目对子模块有依赖关系,却又并不关心子模块的内部开发流程细节。这种情况下,通常不会把所有源码都放在同一个 Git 仓库中。

有一种比较简单的方式,是在当前工作目录下,将子模块文件夹加入到 .gitignore 文件内容中,这样主项目就能够无视子项目的存在。这样做有一个弊端就是,使用主项目的人需要有一个先验知识:需要在当前目录下放置一份某版本的子模块代码。

还有另外一种方式可供借鉴,可以使用 Git 的 submodule 功能,Git 工具的 submodule 功能就是建立了当前项目与子模块之间的依赖关系:子模块路径、子模块的远程仓库、子模块的版本号。

2.使用流程

假定我们有两个项目:project-main 和 project-sub-1,其中 project-main 表示主项目,而 project-sub-1 表示子模块项目。

(1)创建 submodule

使用 git submodule add <submodule_url> 命令可以在项目中创建一个子模块。此时项目仓库中会多出两个文件:.gitmodules 和 project-sub-1 。

通常此时可以使用 git commit -m "add submodule xxx" 提交一次,表示引入了某个子模块。提交后,在主项目仓库中,会显示出子模块文件夹,并带上其所在仓库的版本号。

①git submodule add -f -b 分支名 --name 模块名称 [gitlab地址,建议使用ssh方式] contrib/插件目录名称

②将.gitmodules、 contrib/插件目录名称(此改动是一个特殊的标识,不会显示为其下文件) 两个改动提交

(2) 获取 submodule

上述步骤在创建子模块的过程中,会自动将相关代码克隆到对应路径,但对于后续使用者而言,对于主项目使用普通的 clone 操作并不会拉取到子模块中的实际代码。

如果希望子模块代码也获取到,一种方式是在克隆主项目的时候带上参数 --recurse-submodules(或--recursive),这样会递归地将项目中所有子模块的代码拉取。

另外一种可行的方式是,在当前主项目中执行:

git submodule init && git submodule update

拉取单个子模块:git submodule update -- 路径(contrib/sr_plan)

(初始化本地配置文件) &&(从该项目中抓取所有数据并检出父项目中列出的合适的提交)

则会根据主项目的配置信息,拉取更新子模块中的代码。

(3)子模块内容的更新

对于子模块而言,并不需要知道引用自己的主项目的存在。对于自身来讲,子模块就是一个完整的 Git 仓库,按照正常的 Git 代码管理规范操作即可。

对于主项目而言,子模块的内容发生变动时,通常有三种情况:

1)当前项目下子模块文件夹内的内容发生了未跟踪的内容变动;

这是直接修改子模块文件夹中的代码导致的,此时在主项目中使用 git status 能够看到关于子模块尚未暂存以备提交的变更,但是于主项目而言是无能为力的,使用 git add/commit 对其也不会产生影响。

在此情景下,通常需要进入子模块文件夹,按照子模块内部的版本控制体系提交代码。当提交完成后,主项目的状态则进入了情况2,即当前项目下子模块文件夹内的内容发生了版本变化。

2)当前项目下子模块文件夹内的内容发生了版本变化;

当子模块版本变化时,在主项目中使用 git status 查看仓库状态时,会显示子模块有新的提交。在这种情况下,可以使用 git add/commit 将其添加到主项目的代码提交中,实际的改动就是那个子模块 文件 所表示的版本信息。

通常当子项目更新后,主项目修改其所依赖的版本时,会产生类似这种情景的 commit 提交信息。

3)当前项目下子模块文件夹内的内容没变,远程有更新;

之前曾经提到,主项目可以使用 git submodule update 更新子模块的代码,但那是指 当前主项目文件夹下的子模块目录内容 与 当前主项目记录的子模块版本 不一致时,会参考后者进行更新。

但如今这种情况下,后者 当前主项目记录的子模块版本 还没有变化,在主项目看来当前情况一切正常。此时,需要让主项目主动进入子模块拉取新版代码,进行升级操作。通常流程是:cd project-sub-1 && git pull origin master

(4)删除子模块

git submodule deinit 子模块名 && git rm 子模块名

[GitSlave] 对Git命令的封装,可以简化多项目的Git操作

GitSlave用于管理相关的一个父项目和多个Slave项目。通常,它会将你要执行的Git常规操作顺序的在父项目和Slave项目中执行一遍,所以当你执行pull操作,项目中的所有仓库会顺序执行pull操作。GitSlave是对Git命令的封装,是被设计用于简化多仓库的Git操作,而不是要取代Git。

1.添加Slave项目

通过gits prepare初始化父项目,它会在父项目的根目录下添加一个空的.gitslave文件;然后通过gits attach 仓库名.git  仓库路径来添加Slave项目。

2.克隆带有Slave仓库的项目

gits clone ==  顺序的在父项目与子项目中执行git clone

若一开始你并不知道要克隆的项目带有Slave仓库而直接将父项目克隆下来,此时我们可以通过gits populate来进一步克隆所有Slave仓库。

3.提交项目

我们对父项目和子项目做些修改、提交并将这些改动push到服务端,只需将对应的git命令换成gits,它会顺序在所有仓库中执行对应的git命令

4.更新项目

如果你想更新服务端最新代码,可以使用gits pull命令,它会将所有仓库代码都同步为服务端最新状态。

5.GitSlave的缺点

GitSlave被设计用于包含多个Slave仓库的中等大小项目的开发,其在父项目的.gitslave文件中记录所需子项目的信息,并在所有仓库中顺序执行相应Git操作的设计原理,注定其使用场景有一定局限性:

①GitSlave并不会记录所需子项目的版本,所以其永远只是追踪子项目的最新版本,无法满足父项目基于某一特定版本子项目的场景,而此种场景在开发中却是极为常见。

GitSlave在父项目的.gitslave文件中记录相关子项目的信息,使得父项目本身的提交历史与子项目的增删历史相互交织在一起,一旦子项目增多,父项目的提交历史将变得混乱。

添加远程库

现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。

首先,登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库:

目前,在GitHub上的这个git_test仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。

查看远程库信息:git remote -vv

显示某个远程仓库的信息:git remote show [remote]

关联远程库:git remote add 远程主机名(origin) 仓库地址(git+ssh)

【git remote add 与 git clone】

git remote add只是在.gitconfig中创建一个条目,指定特定URL的名称。你必须有一个现有的git仓库来使用它。故git remote add命令用在git init之后。

git clone通过复制位于指定的URI上的现有git存储库来创建新的git存储库。不需要提前init

下载远程端版本但不合并到HEAD中:git fetch 远程主机名(origin)

将远程库的修改同步到本地:git merge origin/master

把本地库的所有内容推送到远程库的master分支上:git push -u 远程库名(origin) 分支名( master)      

添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库)

把本地库的内容推送到远程,实际上是把当前分支master推送到远程。由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

推送成功后,可以立刻在GitHub页面中看到远程库的内容已经和本地一模一样:

把本地master分支的最新修改推送至GitHub:

git push (-f) <远程主机名> <本地分支名>[:<远程分支名>]

eg.将本地的 master 分支推送到 origin 主机的 master 分支:  git push origin master

删除/解除本地与远程库的关联

1.查看远程库信息:git remote -v

2.删除远程库: git remote rm 远程库名(origin)

此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到GitHub,在后台页面找到删除按钮再删除。


Git文件操作


Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到git库, 不参与版本控制. 通过git add 状态变为Staged.

Staged: 暂存状态. 执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodify状态. 执行git reset HEAD [filename]取消暂存, 文件状态为Modified

Modified: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处, 通过git add可进入暂存staged状态, 使用git checkout 则丢弃修改过, 返回到unmodify状态, 这个git checkout即从库中取出文件, 覆盖当前修改 !

Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为Modified. 如果使用git rm移出版本库, 则成为Untracked文件

1.查看当前工作状态:$ git status [filename]

作用:当我们忘记项目做到哪里的时候,比如上个厕所回来,开个会回来等。可以使用这个命令来判断下一步要干嘛。

2.忽略文件.gitignore

有些时候我们不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等,在主目录下建立.gitignore文件,此文件有如下规则:

忽略文件中的空行或以#开始的行将会被忽略。

可以使用Linux通配符。例如:星号(*)代表任意多个字符,问号(?)代表一个字符,方括号([abc])代表可选字符范围,大括号({string1,string2,...})代表可选的字符串等。

如果名称的最前面有一个感叹号(!),表示例外规则,将不被忽略。

如果名称的最前面是一个路径分隔符(/),表示要忽略的文件在此目录下,而子目录中的文件不忽略。

如果名称的最后面是一个路径分隔符(/),表示要忽略的是此目录下该名称的子目录,而非文件(默认文件或目录都忽略)。


Git版本创建与回退


1.创建文件并添加到暂存区

在git_test目录下创建一个文件code.txt,编辑内容如下:

将工作区文件添加到暂存区:git add(注意必须在Git仓库目录执行(git init除外),在仓库目录外执行是没有意义的)

语法一:$ git add 文件名

语法二:$ git add 文件名1 文件名2 文件名3 ......

语法三:$ git add .    【添加当前目录到暂存区中】

2.创建版本:git commit -m '版本名'

提交本地所有修改:git commit -a

*追加提交信息/提交者: git commit --amend --reset-author,这样会将最新修改和最后一次commit一起commit。

*相同作用的一条命令是:git reset --soft  所需合并的最早的commit“之前”的一次版本号

作用:将该次提交之后的所有commit归为一次commit,用于版本的回退,只进行对commit操作的回退,不影响工作区的文件。当发现之前的提交有错误的代码,推荐使用soft进行处理,这样会把错误的提交日志进行废弃,意思就是通过git log就查看不到那一次错误的提交日志。

*git rebase -i 所需合并的最早的commit“之前”的一次版本号(git rebase -i HEAD~n)

eg. 合并hhhh和lllll

git rebase -i 6e67aa6ff(表示合并 6e67aa6ff 往后的 commit(不包括 6e67aa6ff))

将不需要保存的commit的pick改为s(squash,压缩,表示使用提交但合并到上一次提交中)

e/edit:可修改某次的commit信息

git log可看出,已经合并两次提交为一次

3.查看版本记录:git log

显示所有提交(仅显示提交的hash和message):git log --oneline

显示某个用户的所有提交:git log --author="username"

显示某个文件的所有修改: git log [-p] <file>

查看某次提交/更新所涉及的文件,不是文件内容:git log -3 --stat

继续编辑code.txt,在里面增加一行:

创建版本2并查看版本记录:

4.版本回退

        git reset --hard HEAD^

HEAD表示当前最新版本

HEAD^表示当前版本的前一个版本

HEAD^^表示当前版本的前前个版本

也可以使用HEAD~number表示当前版本的前n个版本

当前位于版本2,回退到版本1

使用git log查看版本记录,发现现在只能看到版本1的记录,code.txt现在只有一行,也就是版本1中code.txt的内容。

假如我们现在又想回到版本2,这个时候怎么办?

通过版本号回到指定版本:git reset --hard  版本号

从上面可以看到版本2版本号为:

现在发现版本2又回来了,可以code.txt里面的内容也恢复到版本2的状态。

通过Reference logs(参考日志)获取版本号:git reflog

可以看到版本2的版本号,我们再使用如下命令进行版本回退,版本重新回到了版本2

三、工作区和暂存区

1.工作区(Working Directory)

电脑中的目录,比如git_test就是一个工作区。

2 版本库(Repository): .git

工作区有一个隐藏目录.git,这个不是工作区,而是git的版本库

git的版本库里存了很多东西,其中最重要的就是称为stage(index)的暂存区,还有为git自动创建的第一个分支master/branches,以及指向master/branches的一个指针叫HEAD

因为我们创建git版本库时,git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改

将文件往git版本库里添加的时候,是分两步执行的:

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支

3.创建文件  --  修改文件  -- git  add  --  git commit

(1)下面在git_test目录下再创建一个文件code2.txt,然后编辑内容如下:

(2)然后再次编辑code.txt内容,在其中加入一行,编辑后内容如下:

(3)使用如下命令查看当前工作树的状态:

(4)使用如下命令把code.txt和code2.txt加入到暂存区,然后再执行git status命令,结果如下:

(5)然后,执行git commit就可以一次性把暂存区的所有修改提交到分支创建一个版本

(6)一旦提交后,如果没有对工作区做任何修改,那么工作区就是“干净”的。执行如下命令可以发现:

4.比较对同一文件修改所产生的difference:git diff filename

git diff 查看尚未暂存(add)的文件更新了哪些部分/显示与上次提交版本文件的不同

git diff --cached     查看已经暂存起来(git add)的文件和上次提交(git commit)的版本之间的差异

git diff ffd98b b8e7b0   查看某两个commit之间的差异

git diff ffd98b:filename b8e7b00:filename 查看两个commit的某个文件之间的差异

5.跳过git add直接提交:git commit -a -m '版本号'

四、管理修改

Git跟踪并管理的是修改,而非文件。它只会提交暂存区(git add)的修改来创建版本

(1)编辑a.txt,并使用git add 命令将其添加到暂存区中

(2)继续编辑a.txt,并在其中添加一行

(3)git commit创建一个版本,并使用git status查看,发现第二次修改code.txt内容之后,并没有将其添加的工作区,所以创建版本的时候并没有被提交

Git管理的是修改,当用git add命令后,在工作区的第一次修改(First.)被放入暂存区,准备提交,但是,在工作区的第二次修改(add new line)并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。

所以,只有将修改后的文件添加至暂存区后再commit才会在当前版本中保存所作的修改。

五、撤销修改

对第二次修改有两种处理办法:要么git add将其添加至暂存区去commit;要么撤销该文件的修改

(1)撤销工作区的修改: git checkout -- 文件名。执行如下命令,发现工作区干净了,第二次的改动内容也没了

(3)撤销暂存区修改:git reset HEAD 文件名,重新放回工作区

现在假定对a.txt的修改已经通过git add到暂存区了:

将其重新放回工作区:

总结:

1.撤销工作区修改:git checkout -- 文件名

2.撤销暂存区修改分两步:

先撤销暂存区修改,将其重新放入工作区:git reset HEAD 文件名

                                    再撤销工作区修改:git checkout -- 文件名

3.已经提交了不合适的修改到版本库时,想要撤销本次提交,则需进行版本回退(或强推覆盖原提交):

             git reset --hard 版本号/HEAD^

六、对比文件的不同

1.对比工作区和某个版本中文件的不同:

(1)继续编辑文件code.txt,在其中添加一行内容

(2)现在要对比工作区中code.txt和HEAD版本中code.txt的不同。使用如下命令:

(3)使用如下命令丢弃工作区的改动

七、删除文件

(1)把目录中的code2.txt删除:

这个时候,git知道删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻提示哪些文件被删除了:

(2)现在有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit提交版本

另一种情况是删错了,可以直接使用git checkout -- code.txt撤销修改操作),这样文件code.txt又回来了。

命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容

八、分支管理

8.1 概念

分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。

如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了git又学会了SVN!

        分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

        现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

8.2 创建与合并分支

        git把之前每次提交的版本串成一条时间线,这条时间线就是一个分支。截止到目前只有一条时间线,在git里,这个分支叫主分支,即master/branches分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。

(1) 一开始的时候,master分支是一条线,git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。

(2)当我们创建新的分支,例如dev时,git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

git创建一个分支很快,因为除了增加一个dev指针,改变HEAD的指向,工作区的文件都没有任何变化。

(3)不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

 (4)假如我们在dev上的工作完成了,就可以把dev合并到master上。git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

git合并分支也很快,就改改指针,工作区内容也不变。

(5)合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

git分支中常用指令:

#列出所有本地分支git branch

#列出所有远程分支git branch -r

#列出所有分支:git branch -a

#新建一个分支:git branch 分支名

修改本地分支名:git branch -m old_branch new_branch

#新建一个分支,并切换到该分支git checkout -b [branch-name]  === git switch -c 分支名

#创建本地分支并拉取远程分支到该分支:git checkout -b 本地分支名  远程分支名

从工作目录中删除所有没有tracked过的文件,能轻易删除掉编译后生成的.o和.exe等文件. 这个在打包要发布一个release的时候非常有用:  git clean [option]

git clean经常和git reset --hard一起结合使用,reset只影响被track过的文件, 所以需要clean来删除没有track过的文件。结合使用这两个命令能让你的工作目录完全回到一个指定的<commit>的状态。

-n:是一次clean的演习, 告诉你哪些文件会被删除. 记住他不会真正的删除文件, 只是一个提醒

-f [path]:删除当前目录下所有没有track过的文件. 他不会删除.gitignore文件里面指定的文件夹和文件, 不管这些文件有没有被track过;

-df:删除当前目录下没有被track过的文件和文件夹

-xf:删除当前目录下所有没有track过的文件. 不管他是否是.gitignore文件里面指定的文件夹和文件

#切换分支:git checkout 分支名 |  git switch 分支名

#合并指定分支到当前分支git merge 指定分支名

#删除本地分支git branch -d [branch-name](不能删除当前所在分支)

#丢弃一个没有被合并过的分支:git branch -D [branch-name]

#删除远程分支git push origin --delete [branch-name]

8.3  解决冲突

合并分支往往也不是一帆风顺的。

 (1)创建一个新分支dev并切换到dev分支上

(2)修改code.txt内容,并进行提交

(3)切换回master分支。

(4)在master的code.txt添加一行内容并进行提交。

现在,master分支和dev分支各自都分别有新的提交,变成了这样:

这种情况下,git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。

 (5)执行如下命令尝试将dev分支合并到master分支上来

git告诉我们,code.txt文件存在冲突,必须手动解决冲突后再提交。 

 (6)git status也可以告诉我们冲突的文件:

(7)查看code.txt的内容。

(8)git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存:

(9)    现在,master分支和dev分支变成了下图所示:

(10)用带参数的git log也可以看到分支的合并情况:git log --graph --pretty=oneline

(11)最后工作完成,可以删除dev分支

8.4  分支管理策略

通常,合并分支时,如果可能,git会用fast forward模式,但是有些快速合并不能成而且合并时没有冲突,这个时候会合并之后并做一次新的提交。合并分支时,加上--no-ff (git merge --no-ff -m "merge with no-ff" dev) 参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

分支策略:

master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活

干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本

git cherry-pick <commit>

在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>命令,把bug提交的修改“复制”到当前分支,避免重复劳动。

如果操作过程中发生代码冲突,Cherry pick 会停下来,让用户决定如何继续操作:

(1)--continue:用户解决代码冲突后,第一步将修改的文件重新加入暂存区(git add .),第二步使用git cherry-pick --continue,让 Cherry pick 过程继续执行。

(2)--abort:发生代码冲突后,放弃合并,回到操作前的样子。

(3)--quit:发生代码冲突后,退出 Cherry pick,但是不回到操作前的样子。

git rebase branch:

两个分支master和feature,其中feature是在提交点B处从master上拉出的分支master上有一个新提交M,feature上有两个新提交C和D:

此时切换到feature分支上,想要把master分支合并到feature分支(这一步的场景就可以类比为我们在自己的分支feature上开发了一段时间了,准备从主干master上拉一下最新改动)

当在feature分支上执行git rebase master时,git会从master和featuer的共同祖先B开始提取feature分支上的修改,也就是C和D两个提交,先提取到。然后将feature分支指向master分支的最新提交上,也就是M。最后把提取的C和D接到M后面,但这个过程是删除原来的C和D,生成新的C’和D’,他们的提交内容一样,但commit id不同。feature自然最后也是指向D’。

退出重置:    git rebase --abort

解决冲突后继续重置: git rebase --continue

已知ip直接克隆:git clone git+ssh://用户名@ip地址:端口号/项目路径 

六、将远程版本合并到本地版本中

git pull 远程主机名  分支名称==git fetch + git merge origin/master

使用上述命令会把远程分支master上的代码下载并合并到本地所在分支

以rebase方式将远端分支与本地合并:    git pull --rebase <remote> <branch>

丢弃在本地的所有改动与提交,到服务器上获取最新的版本历史,并将本地主分支指向它:

git fetch origin &&  git reset --hard origin/master

把当前分支中未提交的修改保存,然后到其他分支去解决bug:

git stash

git checkout bug分支

git stash list:查看stash列表

git stash pop 

拉取对应分支的文件:git clone --branch 分支名 ssh+git

git remote update将更新所有远程分支,但不合并任何更改。

git fetch将仅更新当前所在的分支,而不合并任何更改。

git pull将更新并合并当前所在的当前分支的所有远程更改。


Git打补丁(git patch)


*chery-pick:把其他分支的一次或多次commit,在当前分支上重演。典型的使用场景:其他分支有很多提交,但是你只对其中的一部分感兴趣,这时候可以使用chery-pick,只挑选其他分支感兴趣的commit,合并到自己的分支中。

*format-patch:类似chery-pick,也是把A分支的一次或多次commit提取出来,应用到B分支上的过程。不过,format-patch可以把感兴趣的commit做成补丁文件(可以远程发送给其他人,这是与cherry-pick最大的不同)。这个补丁文件可以被应用到其它分支上。

1.生成patch

git diff生成的UNIX标准补丁.diff文件

git format-patch生成的Git专用.patch 文件

.diff文件与.patch文件的区别就是diff生成的差异文件不附带commit相关的作者、message等信息,只记录差异。

(1) git format-patch   -x:生成从当前commit往前x个commit的patch文件,默认有几个commit就会生成几个.patch文件,并且默认文件名从最早提交的commit 0001编号

(2) git format-patch commit_id  -x:生成从指定commit_id(包含该commit) 往前x个commit的patch文件

git format-patch commit_id  -1:单独生成某个commit的patch

(3) git format-patch start_commit_Id  ..  end_commit_Id:生成两个commit之间的所有commit的patch文件(不包含start_commit)

2.应用patch文件

git apply --stat xxx.patch:检查patch文件

git apply --check xxx.patch:检查能否应用成功

git apply xxx.patch:应用patch文件


git标签


①创建标签:git tag 标签号 commit号

删除标签:git tag -d 标签号

②查看所有标签:git tag

③查看标签信息:git show 标签号

④推送标签到远程:git push origin 标签号

删除远程标签:先从本地删除: git tag -d 标签号

                         再从远程删除:  git push origin   :refs/tags/标签号


解决A compiler with support for C++14 language features is required


Centos7默认gcc版本为4.8,此提示需要更高版本,这里尝试升级到8版本

1.安装centos-release-scl:sudo yum install -y centos-release-scl

2.安装devtoolset:sudo yum install -y devtoolset-8-gcc*

3.激活对应版本的devtoolset(每次开启会话前都需激活,否则仍旧是低版本):source /opt/rh/devtoolset-8/enable

永久升级:

mv /usr/bin/gcc /usr/bin/gcc-4.8.5

ln -s /opt/rh/devtoolset-8/root/bin/gcc /usr/bin/gcc

mv /usr/bin/g++ /usr/bin/g++-4.8.5

ln -s /opt/rh/devtoolset-8/root/bin/g++ /usr/bin/g++

4.查看gcc版本

gcc -v

g++ -v


GPDB简介


一、Greenplum数据库

是业界最快最高性价比的关系型分布式数据库,它在开源的PostgreSQL的基础上采用MPP架构(Massive Parallel Processing,海量并行处理),具有强大的大规模数据分析任务处理能力。

GreenPlum数据库支持分布式事务,支持ACID,保证数据库中数据的强一致性。

GreenPlum数据库采用MPP架构,其基本特征是有多台SMP(Symmetric Multi-Processor,对称多处理器)服务器通过节点互联网络连接而成,是一种Share Nothing(完全无共享)结构,因而扩展能力最强,理论上可以无限扩展。目前的技术可以实现512个节点互联,包含数千个CPU。

GreenPlum数据库 VS传统数据库

使用传统数据库时,我们经常会通过分库分表的方式将数据打散到多个数据库实例中。其缺点在于可能会出现不平均的情况:数据在后端被打散成许多数据分片,但是有些分片的数据量很大,热度很高,有些分片相对来说热度较低。当进行数据统计或分析时,一部分用户数据处理速度慢,一部分用户数据处理速度快,使得许多用户的体验下降。

GreenPlum数据库采用分而治之的方法,将数据非常均衡的分布在所有节点上。而且当服务器数量不够或者计算能力不够的时候,可以在线横向扩展,此时数据库会重新做二次分片,也就是表数据需要重新分布,在保证强大处理能力的同时也时刻保持用户性能的均衡,提升用户体验。

 大规模数据存储

(1)Greenplum数据库通过将数据分布到多个节点上来实现规模数据的存储。数据库的瓶颈经常发生在I/O方面,数据库的诸多性能问题最终总能归罪到I/O身上,久而久之,IO瓶颈成为了数据库性能的永恒的话题。

(2)Greenplum采用分而治之的办法,将数据规律的分布到节点上,充分利用Segment主机的IO能力,以此让系统达到最大的IO能力(主要是带宽)。

(3)在Greenplum中每个表都是分布在所有节点上的。Master节点首先通过对表的某个或多个列进行hash运算,然后根据hash结果将表的数据分布到Segment节点中。整个过程中Master节点不存放任何用户数据,只是对客户端进行访问控制和存储表分布逻辑的元数据。


使用QianBaseMPP


一、初始化数据库

qbinitsystem -c /home/qbadmin/qbinitsystem_config

# FILE NAME:qbinitsystem_config

# Naming convention for utility-generated data directories.

SEG_PREFIX=qbseg

# 计算主段端口号的基本数。

PORT_BASE=6000

# 分支目录(此处表示一个segment上三个分支)

declare -a DATA_DIRECTORY=(/home/qbadmin/segment1/primary /home/qbadmin/segment2/primary /home/qbadmin/segment3/primary)

# 操作系统配置的主机名或IP地址的主机

#MASTER_HOSTNAME=21341f982684 --容器id

COORDINATOR_HOSTNAME=21341f982684

COORDINATOR_DIRECTORY=/home/qbadmin/coordinator

# 主数据目录的文件系统位置

MASTER_DIRECTORY=/home/qbadmin/masterdata

# 主实例的端口号

MASTER_PORT=20158

# 各segment的分布主机的hostname存于MACHINE_LIST_FILE.txt中

MACHINE_LIST_FILE=/home/qbadmin/MACHINE_LIST_FILE.txt

#用于连接到远程主机的ssh

TRUSTED_SHELL=ssh

DATABASE_NAME=qianbasempp

qbinitsystem_config字段说明:

1.对于primary和mirror的地址路径而言,在一台segment主机上要建立几个节点,就写入几个地址;而且在新建集群初始化的时候,mirror节点的分布是grouped模式,加-S在初始化时候为spread模式

2.如果在新建的集群中,只要primary而想在后续的过程中加入mirror节点的话,就不要修改可选镜像的参数了(#不要删除),一旦开启mirror,在后续的增加节点等,就不能只加入primary节点了,对应的mirror节点也要加入

3.可以通过修改该文件直接初始化一个数据库,就不用在启动gp后,指定系统数据库(如template1)来新建数据库

文件目录:

coordinator====master

环境变量:

export QBHOME=/home/qbadmin/qbdb

export PATH=/home/qbadmin/qbdb/bin:${PATH}

export LD_LIBRARY_PATH=/home/qbadmin/qbdb/lib{LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

export PGDATA=/home/qbadmin/qbdb/data

export MASTER_DATA_DIRECTORY=/home/qbadmin/masterdata/gpseg-1

export COORDINATOR_DATA_DIRECTORY=/home/qbadmin/masterdata/gpseg-1

export PGPORT=5432

export PGUSER=qbadmin

export PGDATABASE=postgres

二、启动数据库

qbstart

三、进入数据库

psql -d qianbase


MPP回归测试


1.重建demo库:cd qbAux/qbdemo && make

2.运行回归程序:

为了确保自己的demo库不影响回归测试,所以将.bashrc里面个人环境变量清除

并且停掉相关进程:

ps -ef | grep qbadmin   

No lock file /tmp/.s.QBSQL.5432 but a process running on port 5432

找到带有自己路径的文件后:pg_ctl -D 文件名 stop

①加载环境变量:source qbAux/qbdemo/qbdemo-env.sh

②设置代理白名单

避免设置了公司代理导致测试无法通过,执行前导出环境变量

export no_proxy="${HOSTNAME},127.0.0.1"

③回归:在顶层目录/Qianbasempp*执行make installcheck

之后删除回归测试产生的数据:

cd /QianBaseMPP-7.0.0-1/qbAux/qbdemo

. qbdemo-env.sh

qbstop

rm -rf datadirs/

3.执行单个test的回归测试

在顶层目录下:

cd src/test/regress && make installcheck-tests EXTRA_TESTS=sql下的模块名

举例比如make installcheck-tests EXTRA_TESTS=copy2

测试可能会失败,因为有的依赖前面的测试


GPDB


一、gpconfig

在Greenplum数据库系统中所有的Segment上设置服务器配置参数。

1.简介

gpconfig工具允许用户在Greenplum数据库系统中所有实例(Master、Segment和镜像)的postgresql.conf文件中设置、复原或查看配置参数。设置参数时,如果需要,还可以为Master指定一个不同的值。例如,诸如max_connections之类的参数要求Master的设置不同于Segment的设置。如果要设置或复原全局参数或仅可对Master设置的参数,请使用 --masteronly选项。

gpconfig只能用来管理某些参数。例如,用户不能使用它来设置port等参数,这些参数对每个Segment实例都不同。使用-l (list)选项查看gpconfig支持的配置参数的完整列表。

当gpconfig在Segment的postgresql.conf文件中设置配置参数时,新的参数设置将总是显示在该文件的底部。当用户使用gpconfig移除配置参数时,gpconfig会在所有Segment的postgresql.conf 文件中把该参数注释掉,从而恢复系统默认设置。例如,如果使用gpconfig删除(注释掉)一个参数,并且稍后把它添加回来(设置新值),则该参数会有两个实例,一个被注释掉,另一个被启用并添加到postgresql.conf文件的底部。

设置参数之后,用户必须重新启动其Greenplum数据库系统,或者重新加载postgresql.conf 文件以使得更改生效。是否需要重新启动或者加载取决于被设置的参数。

要显示系统中当前参数的设置值,请使用-s选项。

gpconfig使用以下环境变量连接到Greenplum数据库的Master实例并获取系统配置信息:

PGHOST

PGPORT

PGUSER

PGPASSWORD

PGDATABASE

2.选项

-c | --change param_name

所要修改的参数名,如shared_preload_libraries。在postgresql.conf 文件的底部添加。

-v | --value value

用于由-c选项指定的配置参数的值。默认情况下,此值将应用于所有Segment及其镜像、Master和后备Master。

-m | --mastervalue master_value

用于由-c选项指定的配置参数的Master值。如果指定,则该值仅适用于Master和后备Master。该选项只能与-v一起使用。

--masteronly

当被指定时,gpconfig 将仅编辑Master的postgresql.conf文件。

-r | --remove param_name

通过注释掉postgresql.conf文件中的项删除配置参数。

-l | --list

列出所有被gpconfig工具支持的配置参数。

-s | --show param_name

显示在Greenplum数据库系统中所有实例(Master和Segment)上使用的配置参数的值。如果实例中参数值存在差异,则工具将显示错误消息。使用-s选项运行gpconfig将直接从数据库中读取参数值,而不是从postgresql.conf文件中读取。如果用户使用gpconfig 在所有Segment中设置配置参数,然后运行gpconfig -s来验证更改,用户仍可能会看到以前的(旧)值。用户必须重新加载配置文件(gpstop -u)或重新启动系统(gpstop -r)以使更改生效。

--file

对于配置参数,显示在Greenplum数据库系统中的所有Segment(Master和Segment)上的postgresql.conf文件中的值。如果实例中的参数值存在差异,则工具会显示一个消息。必须与-s选项一起指定。

例如,使用ALTER ROLE为用户设置配置参数statement_mem为64MB,而postgresql.conf文件中的值为128MB。运行命令 gpconfig -s statement_mem --file显示 128MB。用户运行的命令gpconfig -s statement_mem显示64MB。

与--file-compare选项一起指定时无效。

--file-compare

对于配置参数,将当前Greenplum数据库值与主机(Master和Segment)上postgresql.conf文件中的值进行比较。postgresql.conf文件中的值表示重新启动Greenplum数据库时的值。

如果值不一样,该工具显示来自所有主机的值,如果所有主机的值一样,则该程序显示摘要报告。

与--file选项一起指定时无效。

--skipvalidation

覆盖gpconfig的系统验证检查,并允许用户对任何服务器配置参数进行操作,包括隐藏参数和gpconfig无法更改的受限参数。当与-l选项(列表)一起使用时,它显示受限参数的列表。

警告: 使用此选项设置配置参数时要格外小心。

--verbose

在gpconfig命令执行期间显示额外的日志信息。

--debug

设置日志输出级别为调试级别。

-? | -h | --help

显示在线帮助。


gitlab使用教程


一、简介

Git- 它是一个源代码版本控制系统,在本地跟踪更改并从远程资源推送或提取更改。

GitLabGitHubBitbucket- 提供远程访问Git存储库的服务。 除了托管代码之外,这些服务还提供用来帮助管理软件开发生命周期的附加功能。 这些附加功能包括管理不同人之间的代码共享,错误跟踪,wiki空间和其他“社交编码”工具。

GitHub是一项公开可用的免费服务,它要求所有代码(除非您有付费帐户)公开。 任何人都可以看到您推送给GitHub的代码并提供改进建议。 GitHub目前承载数以万计的开源项目的源代码。

GitLab是一种类似github的服务,组织可以使用它来提供git存储库的内部管理。 它是一个自我托管的Git-repository管理系统,可以保持用户代码的私密性,并且可以轻松地部署代码的更改。

二、研发流程

1.维护者fork项目代码

2.维护者关闭维护者空间下的CI/CD

需要关闭CI/CD,因为在提交MR的时候,Gitlab会自动启用CI,因为CI没有配置,所以CI肯定无法生效,会导致MR合并失败,所以需要手动关闭CI。

3.维护者将开发者加入维护者的项目

4.开发者从维护者的EsgynDB项目Fork代码,关闭开发空间的CI/CD选项

当维护者将开发者加入项目后,开发者会获得推送branch权限,维护者需要将项目地址发送给开发者。开发者获得项目访问权限,将维护者的代码Fork到开发者的空间中,Fork流程参考维护者从主库fork代码流程。开发者关闭开发者空间的CI/CD选项。

5.开发者进行开发

●新建分支

●本地拉取代码后进行开发

●commit

6.提交代码到维护者空间的EsgynDB

●创建Merge Request

刚刚推送的分支是可以从合并请求中看到的,所以只需要点击创建合并请求即可

填写关于此MR的标题以及注释,MR的注释信息是保存在Gitlab中的,不会保存在Git开发代码中,请注意合并分支的目的分支是否正确

提交MR请使用模板,模板名称为mr_template

●提交Merge Request

注意Merge options选项,这个选项是开发者如果将这个分支推送到维护者的分支后,会将开发者空间的这个分支删除,请酌情考虑是否勾选.

7. 维护者合并代码

GitSlave用于管理相关的一个父项目和多个Slave项目。通常,它会将你要执行的Git常规操作顺序在父项目和Slave项目中执行一遍,所以当你执行pull操作,项目中的所有仓库会顺序执行pull操作。GitSlave是对Git命令的封装,是被设计用于简化多仓库的Git操作,而不是要取代Git。我们还是以Hexo博客项目和Hacker主题项目为例来说明GitSlave的用法。


runner


1.找到流水线job,点击faild 的job

2.查看失败日志

点击完整原始日志

定位问题三要素

①Runner所在主机和runner名称

ssh ip            passwd:esg123.mpp

cd      /var/lib/docker/volumes/     runner-ukjtaxr5-project-1225-concurrent-0-cache-c33bcaa1fd2c77edfc3893b41966cea8/_data/20334_29869_3ce71f8a/code/qbAux/qbdemo/datadirs/qbAdminLogs

$ echo $build_dir

/builds/20397_29973_8e19d836

$ echo $code_dir

/builds/20397_29973_8e19d836/BUILD/QianBaseTP

$ echo $exec_root

/builds/20397_29973_8e19d836/BUILDROOT

$ echo $exec_dir

/builds/20397_29973_8e19d836/BUILDROOT/QianBaseTP

$ echo $rpm_dir

/builds/20397_29973_8e19d836/RPMS

问题复现

1.记录runner信息,按上述步骤进入跑回归的ip机器,创建docker(记住需更改runner信息):

docker run -it --rm --ipc=host -v runner-6fgiks5k-project-1190-concurrent-0-cache-c33bcaa1fd2c77edfc3893b41966cea8:/builds reg.esgyn.cn/mpp/gitlab_regress_centos7:x86 sh

2.切到su用户

3.进入代码执行目录:/builds

安装数据库:yum install QianBaseTP-15.0-1.el7.x86_64.rpm,su - qianbase,执行脚本文件

进数据库:

export PATH=/usr/local/QianBaseTP/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

export LD_LIBRARY_PATH=/usr/local/QianBaseTP/lib

export PKG_CONFIG_PATH=/usr/local/QianBaseTP/lib/pkgconfig

qsql -p 20158 -U qbadmin -d qianbase


.gitlab-ci.yml


.gitlab-ci.yml 文件被用来管理项目的 runner 任务。GitLab CI 使用YAML文件 (.gitlab-ci.yml) 来管理项目配置。该文件存放于项目仓库的根目录,它定义该项目如何构建。

开始构建之前YAML文件定义了一系列带有约束说明的任务。这些任务都是以任务名开始并且至少要包含script部分:

上面这个例子就是一个最简单且带有两个独立任务的CI配置,每个任务分别执行不同的命令。script可以直接执行系统命令(例如:./configure && make && make install)或者是直接执行脚本(test.sh)。

用下面这个例子来说明YAML语法还有更多复杂的任务:

下面列出保留字段,这些保留字段不能被定义为job名称:

image和services

这两个关键字允许使用一个自定义的Docker镜像和一系列的服务,并且可以用于整个job周期。

before_script

before_script用来定义所有job之前运行的命令,包括deploy(部署) jobs,但是在修复artifacts之后。它可以是一个数组或者是多行字符串。

after_script

after_script用来定义所有job之后运行的命令。它必须是一个数组或者是多行字符串

stages

stages用来定义可以被job调用的stages。stages的规范允许有灵活的多级pipelines。

stages中的元素顺序决定了对应job的执行顺序:

1. 相同stage的job可以平行执行。

2. 下一个stage的job会在前一个stage的job成功后开始执行。

①首先,所有build的jobs都是并行执行的。

②所有build的jobs执行成功后,test的jobs才会开始并行执行。

所有test的jobs执行成功,deploy的jobs才会开始并行执行。

最后一个jobs执行成功(deply),commit才会标记为success

任何一个前置的jobs失败了,commit会标记为failed并且下一个stages的jobs都不会执行

variables

GItLab CI 允许在.gitlab-ci.yml文件中添加变量,并在job环境中起作用。因为这些配置是存储在git仓库中,所以最好是存储项目的非敏感配置,例如:

这些变量可以被后续的命令和脚本使用。服务容器也可以使用YAML中定义的变量,因此我们可以很好的调控服务容器。变量也可以定义成job level

除了用户自定义的变量外,Runner也可以定义它自己的变量CI_COMMIT_REG_NAME就是一个很好的例子,它的值表示用于构建项目的分支或tag名称。除了在.gitlab-ci.yml中设置变量外,还有可以通过GitLab的界面上设置私有变量。

cache

从GitLab 9.0开始,pipelines和job就默认开启了缓存:如果cache定义在jobs的作用域之外,那么它就是全局缓存,所有jobs都可以使用该缓存。

缓存binaries和.config中的所有文件:

缓存git中没有被跟踪的文件:

缓存binaries下没有被git跟踪的文件:

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

推荐阅读更多精彩内容