0%

如果涉及到在多个系统里面操作,有可能会不经意间修改了文件的权限。

如果是使用git版本管理软件来管理的话,明明文件没有修改,也会因为文件权限而导致需要解决这些冲突,为什么呢,因为git把文件权限也算作文件差异的一部分。

如果权限的更改不纳入到版本可中,解决办法如下:

1
$ git config core.filemode false

这时候再更新代码就OK了。

处理权限

Unix系统对拥有者、组、其他几种用户都有明确的读写执行权限,这些在windows上没有对应的机制,这个问题在使用git时表现为没有修改文件却出现很多modified的文件,git status显示 typechange。一般出现在增加可执行权限的文件上。git对此也有一定的策略,可以设置core.filemode为false,这样就会忽略文件权限带来的改变。

1
$ git config --add core.filemode false

这样设置后就可以避免下面的问题出现

1
2
3
diff --git a/a.py b/a.py
old mode 100755
new mode 100644

如果clone前没有设置,导致已经进行了修改,可以用下面的命令来批量恢复这些修改。

1
2
$ git status | grep typechange | awk '{print $2}' | xargs git checkout

gitbook 转换为pdf文件

  1. 确保全局安装gitbook-cli:**npm install gitbook-cli -g**
  2. 安装**Calibre**
  3. 运行**gitbook pdf ./..path../BookFloder ./..path../xxx.pdf**
  4. 运行**gitbook epub ./..path../BookFloder ./..path../xxx.pdf**
  5. 运行**gitbook mobi ./..path../BookFloder ./..path../xxx.pdf**

使用node.js制作书籍

  1. 安装node.js
  2. npm install gitbook -g
  3. npm install gitbook-cli -g
  4. gitbook init
  5. 新建章节文件夹,在SUMMARY.md里建立映射关系
  6. 创建book.json,设置参数
  7. gitbook serve 会编译书籍并建立一个临时服务器,可以再浏览器浏览
  8. git来commit和push

InstallRequiredError: “ebook-convert” is not installed.

1
ln -s /Applications/calibre.app/Contents/MacOS/ebook-convert /usr/local/bin

起步

版本控制

版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

大多数人使用的方法就是本地文件改名,然后加上时间,好处是比较简单,坏处就是一旦混淆所在的工作目录,或者一旦丢失弄错了文件就没有办法撤销了。

所以出现了下面的版本控制系统:

  • 本地版本控制系统:rcs,工作原理就是保存并管理文件补丁patch,根据每次的补丁,计算出各个版本的文件内容
  • 集中化的版本控制系统:CVS/SVN等,有一个单一的集中管理的服务器,保存所有文件的修订版本,协同工作的人们通过客户端连接到这台服务器,取出最新的文件或者提交更新
  • 分布式版本控制系统,Git/Mercurial等,客户端把代码仓库完整地镜像下来,每次都是对代码仓库的完整备份

Git 基础

直接记录快照,而非差异比较

Git和其他其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。

Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一链接。

近乎所有操作都是本地执行

在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网。但如果用 CVCS 的话,差不多所有操作都需要连接网络。因为 Git 在本地磁盘上就保存着所有当前项目的历史更新,所以处理起来速度飞快。

时刻保持数据完整性

在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为 Git 的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。

文件的三种状态

对于任何一个文件,在 Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)。

  • 已提交表示该文件已经被安全地保存在本地数据库中了;
  • 已修改表示修改了某个文件,但还没有提交保存;
  • 已暂存表示把已修改的文件放在下次提交时要保存的清单中。

由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的工作目录,暂存区域,以及本地仓库。

初次运行 Git 前的配置

一般在新的系统上,我们都需要先配置下自己的 Git 工作环境。配置工作只需一次,以后升级时还会沿用现在的配置。当然,如果需要,你随时可以用相同的命令修改已有的配置。

Git 提供了一个叫做 git config 的工具(译注:实际是 git-config 命令,只不过可以通过 git 加一个名字来呼叫此命令。),专门用来配置或读取相应的工作环境变量。而正是由这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方:

  • /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 –system 选项,读写的就是这个文件。
  • ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 –global 选项,读写的就是这个文件。
  • 当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。

用户信息

第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录:

1
2
$ git config --global user.name "Your name"
$ git config --global user.email youremail@example.com

如果用了 –global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。
如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 –global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。

文本编辑器

接下来要设置的是默认使用的文本编辑器。Git 需要你输入一些额外消息的时候,会自动调用一个外部文本编辑器给你用。默认会使用操作系统指定的默认编辑器,一般可能会是 Vi 或者 Vim。如果你有其他偏好,比如 Emacs 的话,可以重新设置:

1
$ git config --global core.editor emacs

差异分析工具

还有一个比较常用的是,在解决合并冲突时使用哪种差异分析工具。比如要改用 vimdiff 的话:

1
$ git config --global merge.tool vimdiff

Git 可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合并工具的输出信息。当然,你也可以指定使用自己开发的工具,具体怎么做可以参阅第七章。

查看配置信息

要检查已有的配置信息,可以使用 git config –list 命令:

1
$ git config --list

Git 基础

要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff

若要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用 git diff --cached 命令。(Git 1.6.1 及更高版本还允许使用 git diff –staged,效果是相同的,但更好记些。

跳过使用暂存区域

尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤

移除文件

要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。

另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者一堆 .a 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,以便稍后在 .gitignore 文件中补上,用 –cached 选项即可:

1
$ git rm --cached readme.txt

移动文件

运行 git mv 就相当于运行了下面三条命令:

1
2
3
$ mv README.txt README
$ git rm README.txt
$ git add README

查看提交历史

在提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,可以使用 git log 命令查看。

我们常用 -p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新。

在做代码审查,或者要快速浏览其他协作者提交的更新都作了哪些改动时,就可以用这个选项。此外,还有许多摘要选项可以用,比如 –stat,仅显示简要的增改行数统计。

每个提交都列出了修改过的文件,以及其中添加和移除的行数,并在最后列出所有增减行数小计。还有个常用的 –pretty 选项,可以指定使用完全不同于默认格式的方式展示提交历史。比如用 oneline 将每个提交放在一行显示,这在提交数很大时非常有用。另外还有 short,full 和 fuller 可以用,展示的信息或多或少有些不同。

但最有意思的是 format,可以定制要显示的记录格式,这样的输出便于后期编程提取分析,像这样:

1
$ git log --pretty=format:"%h - %an, %ar : %s"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明

用 oneline 或 format 时结合 –graph 选项,可以看到开头多出一些 ASCII 字符串表示的简单图形,形象地展示了每个提交所在的分支及其分化衍合情况。

git log 支持的命令选项

选项 说明
-p 按补丁格式显示每个更新之间的差异。
–stat 显示每次更新的文件修改统计信息。
–shortstat 只显示 –stat 中最后的行数修改添加移除统计。
–name-only 仅在提交信息后显示已修改的文件清单。
–name-status 显示新增、修改、删除的文件清单。
–abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
–relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
–graph 显示 ASCII 图形表示的分支合并历史。
–pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。

限制输出长度

除了定制输出格式的选项之外,git log 还有许多非常实用的限制输出长度的选项,也就是只输出部分提交信息。之前我们已经看到过 -2 了,它只显示最近的两条提交,实际上,这是 - 选项的写法,其中的 n 可以是任何自然数,表示仅显示最近的若干条提交。不过实践中我们是不太用这个选项的,Git 在输出所有提交时会自动调用分页程序(less),要看更早的更新只需翻到下页即可。

另外还有按照时间作限制的选项,比如 –since 和 –until。下面的命令列出所有最近两周内的提交:

1
$ git log --since=2.weeks

你可以给出各种时间格式,比如说具体的某一天(“2008-01-15”),或者是多久以前(“2 years 1 day 3 minutes ago”)。

还可以给出若干搜索条件,列出符合的提交。用 --author 选项显示指定作者的提交,用 --grep 选项搜索提交说明中的关键字。(请注意,如果要得到同时满足这两个选项搜索条件的提交,就必须用 –all-match 选项。否则,满足任意一个条件的提交都会被匹配出来)

另一个真正实用的git log选项是路径(path),如果只关心某些文件或者目录的历史提交,可以在 git log 选项的最后指定它们的路径。因为是放在最后位置上的选项,所以用两个短划线(–)隔开之前的选项和后面限定的路径名。

撤消操作

任何时候,你都有可能需要撤消刚才所做的某些操作。接下来,我们会介绍一些基本的撤消操作相关的命令。请注意,有些撤销操作是不可逆的,所以请务必谨慎小心,一旦失误,就有可能丢失部分工作成果。

修改最后一次提交

有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以使用 –amend 选项重新提交:

1
$ git commit --amend

此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行此命令的话,相当于有机会重新编辑提交说明,但将要提交的文件快照和之前的一样。

启动文本编辑器后,会看到上次提交时的说明,编辑它确认没问题后保存退出,就会使用新的提交说明覆盖刚才失误的提交。

如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 –amend 提交:

1
2
3
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

上面的三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。

取消已经暂存的文件

1
$ git reset HEAD <filename>

取消对文件的修改

1
$ git checkout -- <filename>

这个命令将丢弃所有的修改,特别危险,所有对文件的修改都没有了,所以只有百分比确定的时候,在使用该命令。

远程仓库的使用

查看当前的远程库

可以使用git remote命令来列出当前的远程库,或者git remove -v来显示对应的克隆地址。

推送数据到远程仓库

1
git push [remote-name] [branch-name]

查看远程仓库信息

1
$ git remote show origin

远程仓库的删除和重命名

1
$ git remote rename old new

碰到远端仓库服务器迁移,或者原来的克隆镜像不再使用,又或者某个参与者不再贡献代码,那么需要移除对应的远端仓库,可以运行 git remote rm 命令:

1
$ git remote rm [remote-name]

Git 使用说明的各种比较好的文章或者书籍:

什么是github

好多人会把git与github搞混淆,两个是截然不同的东西。

git是一套版本管理软件,而github是托管git仓库的平台。

另外github还提供了很多好的特性,比如Pull Request、issue、wiki等等。

方便大家协作开发,共同进步。

添加远端仓库

有时一个仓库可能有多个分支或者多个维护者版本,此时我们可能希望跟踪多个版本。

如何解决呢,就需要添加远程仓库。

可以使用一个简单的名字,以便将来引用,运行 git remote add [shortname] [url]

然后使用$ git remote -v就可以看到远端分支与对应的网址了,
此时就可以使用git fetch [shortname]或者git pull [shortname]来抓取更新了。

从远程仓库抓取数据

正如之前所说的,可以用下面的命令从远程仓库抓取数据到本地:

1
2
$ git fetch [remote-name]
$ git pull [remote-name]

那么这两个命令的区别在那里呢?

如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,git fetch origin 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch 以来别人提交的更新)。有一点很重要,需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。

如果设置了某个分支用于跟踪某个远端仓库的分支,可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。在日常工作中我们经常这么用,既快且好。实际上,默认情况下 git clone 命令本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支(假设远程仓库确实有 master 分支)。所以一般我们运行 git pull,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录中的当前分支。

Linux下的off_t类型

off_t类型用于指示文件的偏移量,常就是long类型,其默认为一个32位的整数,在gcc编译中会被编译为long int类型,在64位的Linux系统中则会被编译为long long int,这是一个64位的整数,其定义在unistd.h头文件中可以查看。

1
2
3
4
5
6
7
8
9
10
11
12
# ifndef __off_t_defined
# ifndef __USE_FILE_OFFSET64
typedef __off_t off_t;
# else
typedef __off64_t off_t;
# endif
# define __off_t_defined
# endif
# if defined __USE_LARGEFILE64 && !defined __off64_t_defined
typedef __off64_t off64_t;
# define __off64_t_defined
# endif

git修改远程仓库地址

有些情况下git的远程地址可能有变动,那么此时就需要修改找个repo地址。

方法有下面三种:

修改命令

git remote set-url origin [url]
例如:git remote set-url origin new-address.git

先删除再添加

git remote rm origin
git remote add origin [url]

直接修改config文件

打开当前目录的.git/config文件,修改对应的地址即可。

读写二进制文件

需要的头文件

1
2
3
4
5
#include <QMessageBox>
#include <QString>
#include <QPixmap>
#include <QFile>
#include <QDataStream>

写二进制文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool write_file(const QString &filename)
{
QFile file(filename);
if(!file.open(QIODevice::WriteOnly))
{
QMessageBox::warning(this,tr("My App"),tr("Cannot write file %1:\n%2 .").arg(file.fileName()).arg(file.errorString()));
return false;
}

QDataStream out(&file);
out.setVersion(QDataStream::Qt_5_7);

out << qint32(0x1234abcd);

QApplication::setOverrideCursor(Qt::WaitCursor);

for (int i;i<100;i++)
out << quint16(i);

QApplication::setOverrideCursor(Qt::ArrowCursor);
return true;
}

读二进制文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bool read_file(const QString &filename)
{
QFile file(filename);
if(!file.open(QIODevice::ReadOnly))
{
QMessageBox::warning(this,tr("My App"),tr("Cannot read file %1:\n%2.").arg(file.fileName()).arg(file.errorString()));
return false;
}

QDataStream in(&file);
in.setVersion(QDataStream::Qt_5_7);

//in << qint32(0x1234abcd);
qint32 magic;
in >> magic;

if (magic != 0x1234abcd)
{
QMessageBox::warning(this,tr("My App"),tr("This is not a correct file"));
return false;
}

QApplication::setOverrideCursor(Qt::WaitCursor);

quint16 temp;

while (!in.atEnd()) {
in >> temp;
ui->textEdit->setText(QString("%1").arg(temp));
}

QApplication::setOverrideCursor(Qt::ArrowCursor);
return true;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <QApplication>
#include <QSplashScreen>
#include <QDateTime>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QSplashScreen *splash = new QSplashScreen;

splash->setPixmap(QPixmap(":/images/china.jpg"));
//splash->setWindowFlags(Qt::WindowStaysOnTopHint);
splash->show();

splash->showMessage("Will start in 1 seconds",Qt::AlignTop,Qt::blue);

QDateTime n=QDateTime::currentDateTime();
QDateTime now;
do{
now=QDateTime::currentDateTime();
a.processEvents();
} while (n.secsTo(now)<=2);


splash->showMessage("Started...",Qt::AlignTop,Qt::blue);

do{
now=QDateTime::currentDateTime();
a.processEvents();
} while (n.secsTo(now)<=2);

MainWindow w;
w.show();
splash->finish(&w);
delete splash;

return a.exec();
}