git ssh https 踩坑记 —- 域账号密码更新

前几天突然通知要更新公司的域账号密码,然后git pull就一直报

fatal: Authentication failed for ‘https://git ...

很奇怪的是,有一个项目git pull push都是好的。

上网查了下用以下命令可以看到

git remote get-url origin

好的项目是用了ssh,不好的项目用的是https,https把旧的密码存到电脑里了,所以一直登陆不上

解决方法1–修改密码:

// 清除账户密码缓存
git config --system --unset credential.helper
// 设置账户密码永久保存
git config --global credential.helper store
// 拉代码,重新输入账号密码登陆一次,以后再也不用再输账号密码
git pull

解决方法2–改用ssh方式认证:

// 去除https源
git remote remove origin
// 添加ssh远端源
git remote add origin [email protected]:xxx/xxx.git

ps: 值得注意的是更换origin后,分支要和远端重新关联一下

git branch --set-upstream-to=origin/dev

git常用命令速查表

先进入项目文件夹)通过命令 git init 把这个目录变成git可以管理的仓库

git init

把文件添加到版本库中,使用命令 git add .添加到暂存区里面去,不要忘记后面的小数点“.”,意为添加文件夹下的所有文件

git add .

用命令 git commit告诉Git,把文件提交到仓库。引号内为提交说明

git commit -m 'first commit'

关联到远程库

git remote add origin 你的远程库地址

如:

git remote add origin https://github.com/cade8800/ionic-demo.git

获取远程库与本地同步合并(如果远程库不为空必须做这一步,否则后面的提交会失败)

git pull --rebase origin master

把本地库的内容推送到远程,使用 git push命令,实际上是把当前分支master推送到远程。执行此命令后会要求输入用户名、密码,验证通过后即开始上传。

git push -u origin master

状态查询命令

git status

未分类

git 几个好用的命令

git checkout . 撤销工作区修改而未提交的内容(注意:新增的文件不能修改,需要手动删除)

git fetch origin master 将远程仓库的master分支下载到本地当前branch中

git log -p master ..origin/master 比较本地的master分支和origin/master分支的差别

git merge origin/master 合并代码

git push origin feature:master 将本地feature代码推到远端master

注意:

git pull == git fetch + git merge 会出现远程代码直接覆盖本地代码而不报冲突的情况, 推荐上面先git fetch 再对比差异

一巴掌拍平Git中的各种概念

0x000 导读

讲git的文章很多,但是大部分都是一个套路,讲概念,讲命令,太多的概念和命令总是让人有一种稀里糊涂的感觉,讲的很对,但似乎没能讲透,没有醍醐灌顶的感觉,大概是我的悟性差吧。所以这几天一直在做各种git的实验,并且阅读了一些博客、文档、资料,综合下来整理出了这么一篇文章。注意:

  • 本篇文章旨在说明我对git的理解,只是一家之言,聊以分享。
  • 本片文章不是一篇命令教程,或者概念说明,需要一定的git使用经验和踩坑经验。
  • 为了阅读方便,commitID只保留4位

0x001 总结[提前]

这是一篇比较乱七八糟的文章,不从传统出发,尝试用自己的思想去理解git这一神奇的工具。以前我觉得git是命运石之门,我们在不同的时间线(分支)上跳跃,所有的事件都必须且只能发生在时间线上。但是现在我觉得git是无限的可能性的集合,一个事件可以引申出无限的可能性。而我们要做的是用工具(branch、tag、reset、rebase、merge….)来组织这些可能性,从而构成一个有序的、向前发展的历史,引导整个历史的发展,构建可以走向未来的工程。

0x002 存档和读档

  • 存档

其实吧,版本就是存档,就是游戏中的存档,我们不断的推进项目,完成一个又一个任务,然后中途不断的存档,就形成了版本迭代。而版本多了,自然就需要管理了,自然就有了版本管理系统。

在游戏中,存档可以手动存档,也可以到指定点存档,也可以自动定场景存档。在游戏中,存档之后形成的东西叫做档案,而在git中,叫做commit。我们可以使用git add+git commit完成一个档案的创建,或者说版本的创建。

一个commit拥有许多的属性,比如ID、Message、Date、Author:等等,这些信息都有助于我们了解这个版本,就像游戏中的存档会以关卡名/图片之类的信息来提示你这个存档所代表的进度,比如使用git log可以获取以下信息:

commit 4963 (HEAD -> master)
Author: **********
Date:   Thu Jan 10 15:22:12 2019 +0800

    版本H

commit 1a42
Author: **********
Date:   Thu Jan 10 15:25:01 2019 +0800

    版本G

commit 931b
Author: **********
Date:   Thu Jan 10 15:24:50 2019 +0800

    版本F
....
  • 读档

既然有存档,那就有读档。游戏中直接选择一个档案就行了,那git中呢?如果有可视化操作工具,那我们直接点击也是可以的,但现在不使用工具,而使用命令行,该如何呢。读取一个存档说白了在git中就是读取一个commit而已,所以我们可以使用git checkout和git reset两个命令来做到,那如何指定版本呢?前面提到的commit属性中的ID可以帮我们实现这个目标。

环境说明:我在仓库中git commit了8个,每个commit都添加了一个从a-h的文件,并在commit信息中添加版本标记

未分类

使用git checkout切到版本A,可以发现,此时只有文件a

$ git checkout 401e
Note: checking out '401e'.

...

HEAD is now at 401e1b6 版本A

$ ls
README.md       a.txt

使用git reset切换到版本G,可以发现,此时有了a-g几个文件了

$ git reset 1a42
Unstaged changes after reset:
D       b.txt
D       c.txt
D       d.txt
D       e.txt
D       f.txt
D       g.txt

$ git stash
Saved working directory and index state WIP on (no branch): 1a4268d 版本G

l$ ls
README.md       a.txt           b.txt           c.txt           d.txt           e.txt           f.txt           g.txt
  • 总结:
    我们通过commit的ID属性配合其他命令来达到了任意读档的目的,可以在各种版本中随意穿梭,快乐的很啊。而读档的姿势其实还有很多,但不外乎是对commit操作方式的不同,在git中,我觉得commit 才是构成整个版本管理的基本栗子。每一个commit都是独立的个体,虽然和其他commit存在着关联,但是依旧是独立的,而我们在commit构成节点序列中来回移动和操作,就可以达到所有对版本管理的目的。

0x003 别名系统

在上一个章节中,我们已经可以依靠一些命令和commit ID做到在版本中自由的穿梭,但是却带来一个问题,那就是commit ID的记忆难度。commit ID是hash值,尽管git支持只提供前几位就能匹配到hash,并且也提供了commit message来说明commit,但是依旧存在commit的辨识和记忆问题。而这个问题,可以通过别名系统来解决。

所谓的别名系统,其实是我自己归纳的概念,指的其实就是HEAD、branch、tag这三个东西。在我看来,这三个东西都是一样的东西,都是别名,也就是标记一个commit的东西而已,只是在行为表现上有一些区别。

1. HEAD

一个仓库只有一个HEAD,指向你当前所在的commit。如果你创建了一个commit,HEAD将会指向这个新的commit。也可以通过命令,强制HEAD指向某个commit,比如reset、checkout。也就是不论你在哪个commit之上,那么HEAD就在哪儿,或者说,其实你能在哪个commit,是通过修改HEAD指向的commit实现的。

通过修改HEAD在各个版本之间旋转跳跃

未分类

2. branch

一开始我觉得这个东西才是git的核心,因为创建项目的时候,我们就处于master分支之上,并且我们在工作中,往往也是基于分支工作的。但是后来发现,分支在本质上毫无意义,并不需要真的基于branch去工作,基于commit就行了。而branch只是提供了一个方式来管理这些commit。branch和HEAD相同点是随着新的commit的创建,branch指向的commit会不断更新,当然前提是你需要在这个branch所在的commit上创建新的commit。而branch和HEAD的不同点在于HEAD只能有一个,branch可以有多个。

实验一:用branch来实现切换版本

目前的库情况

$ git log --pretty=oneline
1a42 (HEAD) 版本G
931b 版本F
071d 版本E
0caa 版本D
7855 版本C
1295 版本B
401e 版本A

为版本A-G分别创建一个分支

$ git checkout 1a42 -b G
Switched to a new branch 'G'
$ git checkout 931b -b F
Switched to a new branch 'F'
$ git checkout 071d -b E
Switched to a new branch 'E'
$ git checkout 0caa -b D
Switched to a new branch 'D'
$ git checkout 7855 -b C
Switched to a new branch 'C'
$ git checkout 1295 -b B
Switched to a new branch 'B'
$ git checkout 401e -b A
Switched to a new branch 'A'

$ git log --pretty=oneline
1a42 (HEAD -> G) 版本G
931b (F) 版本F
071d (E) 版本E
0caa (D) 版本D
7855 (C) 版本C
1295 (B) 版本B
401e (A) 版本A

接下来就可以换一种方式在版本之间跳跃了,并且不需要记住或者查询冗长的
commit ID

$ git checkout A
Switched to branch 'A'
$ git checkout B
Switched to branch 'B'
$ git checkout C
Switched to branch 'C'
$ git checkout E
Switched to branch 'E'
$ git checkout F
Switched to branch 'F'
$ git checkout G
Switched to branch 'G'

实验二:分支跟随新的commit

当前库的情况,注意:这里的HEAD -> G表示HEAD指向了branch G,而branch G指向了版本G

$ git log --pretty=oneline
1a42 (HEAD -> G) 版本G
931b (F) 版本F
071d (E) 版本E
0caa (D) 版本D
7855 (C) 版本C
1295 (B) 版本B
401e (A) 版本A

添加一个文件,创建一个commit

$ echo 'h'> h.txt
$ git add h.txt
$ git commit -m '版本H'
[G d346d27] 版本H
1 file changed, 1 insertion(+)
create mode 100644 h.txt

此时查看log,可以看到HEAD和G都指向了版本H,就是所谓的branch跟着commit动,但是它真的是跟着commit动吗?

$ git log --pretty=oneline
d346 (HEAD -> G) 版本H
1a42 版本G
931b (F) 版本F
071d (E) 版本E
0caa (D) 版本D
7855 (C) 版本C
1295 (B) 版本B
401e (A) 版本A

实验三:分支跟着啥动

将HEAD指向版本G的commit,而不是分支G,也就是使用git checkout commitID,而不是使用git checkout branchName,可以看到,此时HEAD不指向G,而是HEAD和G同时指向了版本H的commit。

$ git checkout d346 # 版本 H 的 commitID
$ git log --pretty=oneline
d346 (HEAD, G) 版本H
1a42 版本G
931b (F) 版本F
071d (E) 版本E
0caa (D) 版本D
7855 (C) 版本C
1295 (B) 版本B
401e (A) 版本A

继续创建一个commit,可以看到,这个时候分支G不再跟着commit移动了,所以,只有在HEAD指向branch的时候,branch才会向前移动,也就是只要HEAD来到branch身边,branch就会跟着HEAD跑。

$ echo 'i'> i.txt
$ git add i.txt
$ git commit -m "版本I"
[detached HEAD 2e836eb] 版本I
1 file changed, 1 insertion(+)
create mode 100644 i.txt
$ git log --pretty=oneline
2e83 (HEAD) 版本I
d346 (G) 版本H
1a42 版本G
931b (F) 版本F
071d (E) 版本E
0caa (D) 版本D
7855 (C) 版本C
1295 (B) 版本B
401e (A) 版本A

3. tag

tag是比较特殊的一个别名类型,他无法移动,或者说不推荐移动。一旦一个tag和指向某个coimmit,就不希望它移动,因为tag就是用来标记这个commit的,他是一个孤独而忠诚的守望者,而不像branch,花间游龙似的浪子。

现在库的情况

$ git log --pretty=oneline
1a42 (HEAD, G) 版本G
931b (F) 版本F
071d (E) 版本E
0caa (D) 版本D
7855 (C) 版本C
1295 (B) 版本B
401e (A) 版本A

为每个版本添加一个tag,为了区别分支名,统统加了个T

$ git tag TA A
$ git tag TB B
$ git tag TC C
$ git tag TD D
$ git tag TE E
$ git tag TF F
$ git tag TG G

$ git log --pretty=oneline
1a42 (HEAD, tag: G, G) 版本G
931b (tag: TF, F) 版本F
071d (tag: TE, E) 版本E
0caa (tag: TD, D) 版本D
7855 (tag: TC, C) 版本C
1295 (tag: TB, B) 版本B
401e (tag: TA, A) 版本A

现在又多了一种旋转跳跃的方式了

$ git checkout TA
Previous HEAD position was 1a4268d 版本G
HEAD is now at 401e1b6 版本A
$ git checkout TB
Previous HEAD position was 401e1b6 版本A
HEAD is now at 1295260 版本B
$ git checkout TC
Previous HEAD position was 1295260 版本B
HEAD is now at 7855905 版本C
$ git checkout TD
Previous HEAD position was 7855905 版本C
HEAD is now at 0caa2b7 版本D
$ git checkout TE
Previous HEAD position was 0caa2b7 版本D
HEAD is now at 071d00a 版本E
$ git checkout TF
Previous HEAD position was 071d00a 版本E
HEAD is now at 931b3c9 版本F
$ git checkout TG
Previous HEAD position was 931b3c9 版本F
HEAD is now at 1a4268d 版本G
  • 总结
    所以,不管是HEAD、tag、branch,都是一种别名,除了行为表现上的差别,没有太大的不同,特别是branch和tag,不过都只是提供了一种管理commit的方式。

0x004 分叉

在上一章节中,我们揭开了别名系统的红盖头,这一章,我们就开始探索一下分叉的神秘。

和游戏中的存档一样,有时候一个游戏有许多的选择,这些选择指向了不同的结果。而作为游戏玩家,我们希望能够走完所有的选择,以探索更多的游戏乐趣。所以我们会在做选择的时候存档,而当我们走完一个选择,就会读取这个存档,继续往另一个选择探索。这个时候,就产生了两个不同的剧情走向,这就是分叉。

在git中,其实我们可以有无数的选择,每一个commit可以创建无数的commit,就会引申出无数的可能。

我们遇到了一个抉择,所以需要创建版本,暂时称为版本X吧

$ git log --pretty=oneline
2cae (HEAD) 版本X
....

然后我们选择了走Y,并且沿着Y1一直走到Y3,这是尽头

$ git log --pretty=oneline
d2e0 (HEAD) 版本Y3
4ca8 版本Y2
fcff 版本Y1
2cae 版本X
...

接着我们返回X,并选择另一个选择Z,从Z1走到Z3

$ git checkout 2cae # 切到`版本X`
$ git log --pretty=oneline
16ff (HEAD) 版本Z3
0ca5 版本Z2
b4a7 版本Z1
2cae 版本X
...
  • 总结

可以看到,我们顺着两个选择一直往下发展,在这发展的过程中,我们完全没有用到tag和branch,也就是为了印证commit 是构成 git 世界的基本栗子这一说明。

从git log中,我们看不到了Y走向,那Y真的消失了吗?不是的,我们依旧可以通过Y的commit ID来寻回Y的记录。当然为了方便在YZ中切换,我们可以使用branch来标记一下YZ两个走向,这样就形成了YZ两个branch了,也就是分叉!

那那些没有被branch或者tag标记的commit呢?他们会消失吗?会,也不会。不会是因为不被标记的commit将变成dangling commit,我称之为游离的commit,是git中最孤独的存在,只要我们知道commitID,就会可唤回它。但是很大的可能是我们永远不会记得这么一个不被引用的commit,所以我呼吁,善待每一个commit。会是因为还是可能会被回收的,看这里,git 也有 gc。

0x003 合并

和游戏的存档不同的是,git中的版本可以合并,也就是说我可以在分支Y中做完任务Y1、Y2、Y3,然后分支Z中完成任务Z1、Z2、Z3,然后合并这两个分支,结果回到了X,但是却完成了Y1-y3、Z1-Z3,并拿到了神器Y和Z,这让boss怎么活?

实验一:使用merge合并commit

创建版本O

$ echo O >> o.txt
$ git add o.txt
$ git commit -m '版本O'
[detached HEAD 478fa6d] 版本O
1 file changed, 1 insertion(+)
create mode 100644 o.txt

基于版本O创建版本P1

$ echo P >>p1.txt
$ git add p1.txt
$ git commit -m '版本P1'
[detached HEAD a3ab178] 版本P1
1 file changed, 1 insertion(+)
create mode 100644 p1.txt
$ git log --pretty=oneline
a3ab (HEAD) 版本P1
478f 版本O

基于版本O创建版本P2

$ git checkout 478f # 版本O 的 commitID
$ echo p2 >> p2.txt
$ git add p2.txt
$ git commit -m '版本P2'
[detached HEAD cbccf52] 版本P2
1 file changed, 1 insertion(+)
create mode 100644 p2.txt
$ git log --pretty=oneline
cbcc (HEAD) 版本P2
478f 版本O

合并版本P1到版本P2

$ git merge a3ab # 版本P1 的 commitID
$ git log --pretty=oneline
656a (HEAD) Merge commit 'a3ab' into HEAD
cbcc 版本P2
a3ab 版本P1
478f 版本O

实验三:使用rebase合并

切换到版本P2,在版本P2中使用rebase

$ git checkout cbcc # 版本P2 的 commitID
....
HEAD is now at cbccf52 版本P2
$ git rebase a3ab # 版本P1 的 commitID
First, rewinding head to replay your work on top of it...
Applying: 版本P2
$ git log --pretty=oneline
3bd7 (HEAD) 版本P2
a3ab 版本P1
478f 版本O

实验四:使用cherry-pick合并

切换到版本O,新建版本P3

$ echo 'p3'>> p3.txt
$ git add p3.txt
$ git commig -m '版本P3'
git: 'commig' is not a git command. See 'git --help'.

The most similar command is
        commit
$ git commit -m '版本P3'
[detached HEAD ae09e94] 版本P3
1 file changed, 1 insertion(+)
create mode 100644 p3.txt
$ git log --pretty=oneline
ae09 (HEAD) 版本P3
478f 版本O

切换到版本P2中使用cherry-pick合并版本P3的东西

$ git checkout 3bd7 # 版本P2 的commitID
...
HEAD is now at 3bd7820 版本P2
$ git cherry-pick ae09 # 版本P3 的 commitID
[detached HEAD f9dfba2] 版本P3
Date: Sat Jan 12 11:35:27 2019 +0800
1 file changed, 1 insertion(+)
create mode 100644 p3.txt
$ git log --pretty=oneline
f9df (HEAD) 版本P3
3bd7 版本P2
a3ab 版本P1
478f 版本O
  • 注意:合并中的冲突解决
    合并的过程中可能会出现冲突,比如同时拿到神器P1、P2,但是在P1中卖掉了O之前拿到的装备S,而在P2中则为S镶上了宝石,那么合并之后要怎么处理?是卖掉S?还是保留镶宝石的S?还是镶了宝石再卖掉?深井冰啊!我不要面子的啊…
    所以这里就涉及到了合并的冲突解决,这里不再赘述,不是我要讲的内容。

0x005 变基

这个名词让人想入菲菲啊,每次项目新成员加入,总是会提醒他们注意要变基….

这里不去说merge和rebase的爱恨情仇,只说一些rebase的操作,用rebase来整理commit。

上面说到commit 是构成 git 世界的基本栗子,所以,我们需要掌握一些栗子的操作方式

  • 查看commit,可以使用git log,如果需要寻回忘记的commit,可以使用reflog来尝试看看是否能够找到
$ git log --pretty=oneline
68de (HEAD -> X) Merge branches 'Y' and 'Z' into X
16ff (Z) 版本Z3
...
$ git reflog
23e799e (HEAD) HEAD@{0}: rebase -i (pick): 版本Z
01a10d6 HEAD@{1}: rebase -i (squash): 版本Y
a15dd72 HEAD@{2}: rebase -i (squash): # This is a combination of 2 commits.
b6f2ea3 HEAD@{3}: rebase -i (start): checkout 1004
f4c4ccc HEAD@{4}: rebase -i (abort): updating HEAD
...
  • 创建

创建使用git add+git commit就行了

$ echo error > error.txt
$ git add error.txt
$ git commit -m '一个错误的版本'
[X bc90774] 一个错误的版本
1 file changed, 1 insertion(+)
create mode 100644 error.txt
$ git log --pretty=oneline
bc90 (HEAD -> X) 一个错误的版本
68de Merge branches 'Y' and 'Z' into X
...
  • 更新上一个commit,直接使用git commit –amend
$ echo error2 >> error.txt
$ git add error.txt
$ git commit --amend
// 这里将打开一个vim窗口
一个错误的版本

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Fri Jan 11 17:21:18 2019 +0800
#
# On branch X
# Changes to be committed:
#       new file:   error.txt
#
// 保存退出之后输出
[X d5c4487] 一个错误的版本
Date: Fri Jan 11 17:21:18 2019 +0800
1 file changed, 2 insertions(+)
create mode 100644 error.txt
  • 要更新历史中的commit也是可以做到的,例如需要在版本X中加入文件x1.txt,要使用交互式模式
git rebase -i 2e83 # 指向 版本X 的前一个 commit

此时将打开一个交互式窗口

pick 913b571 版本X
pick 0eca5e3 版本Y1
pick 33a9ca3 版本Y2
pick b95b3ca 版本Y3
pick 839c481 版本Z1
pick 6fb6cb3 版本Z2
pick c28d3e0 版本Z3
...

将版本X前面的pick改为e或者edit,保存,然后退出,这个时候,仓库将会回到版本X的状态,并输出

Stopped at 913b571...  版本X
You can amend the commit now, with

git commit --amend

Once you are satisfied with your changes, run

git rebase --continue

添加文件x1

$ echo x1 > x1.txt
$ git add x*
$ git commit --amend
// 打开交互式窗口可以修改 commit message
$ git rebase --comtinue
Successfully rebased and updated detached HEAD.

此时又回会到原本的版本并且多出了文件x1,就像是在版本X中就已经加入一样

  • 插入一个新的commit,上面的栗子中不使用–amend,就会在X和Y1之间插入一个新的commit
$ git rebase -i 2e83
// 交互式窗口,吧`pick`改为`e`
$ echo x2 > x2.txt
$ git add x2.txt
$ git commit -m '插入一个版本X2'
[detached HEAD 1b4821f] 插入一个版本X2
1 file changed, 1 insertion(+)
create mode 100644 x2.txt
$ git rebase --continue
Successfully rebased and updated detached HEAD.
$ git log --pretty=oneline
30a5 (HEAD) 版本Z3
4b00 版本Z2
cc1d 版本Z1
595e 版本Y3
4456 版本Y2
b6f2 版本Y1
1b48 插入一个版本X2
1004 版本X
  • 删除

删除一个分支可以使用交互式rebase,命令:git rebase -i commitID,这里的commitID必须是你要删除的commit的前一个commit。

$ git rebase -i 68de

此时将会打开一个vim窗口

pick bf2c542 版本Y1
pick 588feec 版本Y2
pick 1b2ae37 版本Y3
pick 38f7cf3 版本Z1
pick 080e442 版本Z2
pick 206a7ae 版本Z3
pick 6b01f70 一个错误的版本

# Rebase 2caeda3..6b01f70 onto 2caeda3 (7 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
...

要删除一个错误的版本,将前面的pick改为d或者drop

d 6b01f70 一个错误的版本

保存退出,输出

Successfully rebased and updated refs/heads/X.

合并多个commit,比如合并Z1-Z3,打开交互式窗口之后,将Z2、Z3的pick改为s

$ git rebase -i 100468330c7819173760938d9e6d4b02f37ba001
// 打开了交互式窗口
pick bf2c542 版本Y1
pick 588feec 版本Y2
pick 1b2ae37 版本Y3
pick 38f7cf3 版本Z1
s 080e442 版本Z2
s 206a7ae 版本Z3

保存退出以后,又打开交互式窗口,显示要合并的commit的message,这里可以修改commit。

# This is a combination of 3 commits.
# This is the 1st commit message:

版本Z1

# This is the commit message #2:

版本Z2

# This is the commit message #3:

版本Z3

这里修改为Z,保存,退出,输出,可以看到,Z1-Z3消失了,取而代之的是Z,对Y1-Y3做操作

detached HEAD f4c4ccc] 版本Z
Date: Fri Jan 11 16:27:00 2019 +0800
1 file changed, 3 insertions(+)
create mode 100644 z.txt
Successfully rebased and updated detached HEAD.
$ git log --pretty=oneline
f4c4 (HEAD) 版本Z
595e 版本Y3
4456 版本Y2
b6f2 版本Y1

$ git rebase -i 1004
[detached HEAD 01a10d6] 版本Y
Date: Fri Jan 11 16:24:37 2019 +0800
1 file changed, 3 insertions(+)
create mode 100644 y.txt
Successfully rebased and updated detached HEAD.
$ git rebase --continue

重新排序commit顺序,比如重排版本Y和版本Z,交换一下顺序就好了

$ git log --pretty=oneline
23e7 (HEAD) 版本Z
01a1 版本Y
$ git rebase -i 1b48

这时候打开交互式窗口,显示

pick a1942a3 版本Y
pick eeabc6c 版本Z

将它交换顺序,保存,退出

pick eeabc6c 版本Z
pick a1942a3 版本Y

查看结果

Successfully rebased and updated detached HEAD.
$ git log --pretty=oneline
a194 (HEAD) 版本Y
eeab 版本Z

如何快速掌握git命令

Git 是程序员工作必备的技能之一,本文将从基本概念、常见操作、快捷技巧三个方面进行介绍,总结工作中常用的 Git 命令,使你能够快速脱离 Git 可视化工具,掌握 Git。

1. 基本概念

1.1 4个区

  • 工作区
  • 暂存区
  • 本地仓库
  • 远程仓库

1.2 5种状态

  • 未修改:对应区为工作区
  • 已修改:对应区为工作区
  • 已暂存:对应区为暂存区
  • 已提交:对应区为本地仓库
  • 已推送:对应区为远程仓库

平时我们修改和未修改的代码都属于工作区,git add 即把已修改的代码暂时存储,代码进入暂存区,git commit 即把暂存的代码提交到本地仓库,产生一条提交记录,git push 即将本地仓库推送到远程仓库,代码实现远程共享。

2. 常见操作

2.1 本地初始化

主要有两种方法,一种是 clone 项目,另一种是本地创建再连接到远程项目。下面以 github 为例:

  • clone 项目
git clone [email protected]:userName/projectName.git
  • 本地创建再连接
cd projectName   // 进入项目文件夹
git init         // 初始化
git remote add origin [email protected]:userName/projectName.git  // 连接到远程 github 项目

第二种方法要首先确保远程仓库已经创建。

2.2 分支创建和提交

通常,代码都在分支上进行开发,分支开发的命令几乎是 Git 中最常用的命令,下面罗列了分支开发中的常用命令并加以说明。

git checkout -b project-dev   // 创建 project-dev 分支
git branch                    // 查看分支
git status                    // 显示工作区和暂存区的状态
git add .                     // 将工作区的所有变化提交到暂存区
git commit -m 'message'       // 将暂存区提交到本地仓库
git checkout dev              // 切换到 dev 分支
git merge project-dev         // 将 project-dev 合并到 dev 分支
git pull                      // 拉取仓库仓库代码
git push origin dev           // 提交本地仓库代码
git branch -d zproject-dev    // 删除 project-dev 分支

切换分支时,记得将代码提交到本地仓库,避免未提交的代码一同切换。

2.3 撤销更改

实际工作中,经常会出现提交错误的情况,所以撤销更改的操作要牢记于心。处于不同状态下的撤销更改是不同的:

  • 已修改,未暂存
# 方法1:撤销所有修改
git checkout .
# 方法2:撤销所有修改
git reset --hard
  • 已暂存,未提交,即 git add 错误
# 撤销指定的文件,保留更改
git reset <file>
# 撤销所有的文件,保留更改
git reset
# 撤销所有的文件,不保留更改
git reset --hard
  • 已提交,未推送,即 git commit 错误文件
# 撤销 commit 操作,保留文件变化
git reset HEAD~1
# 撤销 commit 操作,删除变化
git reset --hard HEAD~1
  • 已推送,即 git push 错误文件
# 退回到指定版本
git reset --hard <commit_id>
# 强制覆盖远程分支
git push -f

2.4 指定版本修复

线上代码出现问题,需要紧急修复,而 dev 分支上正在开发下一个版本,此时我们就要新开分支,来进行代码修复,并合并到线上分支。

git branch -a                                   // 查看所有分支
git checkout -b repair_dev origin/repair_dev    // 创建远程分支 repair_dev,并切换到 repair_dev分支
...                                             // 在分支上进行修改提交
git checkout dev                                // 切换回 dev 分支
git merge repair_dev                            // 合并分支 repair_dev
git branch -D repair_dev                        // 删除分支

3. 快捷技巧

从常用操作中可以看出,在 git 的使用过程中,部分命令的使用非常频繁,例如

git status
git add .
git commit -m msg

每次的重复输入操作很繁琐,为此 git 提供别名 alias 的设置,使用 alias 别名设置可以简化我们的操作

3.1 alias

git config --global alias.ci commit

设置之后,以后提交到本地仓库就简写为

git ci -m msg

看起来是不是简短了很多?但这种方法每次只能简化一个命令,主要是用来简化参数。

3.2 自定义指令

我们可以通过配置相应文件,来简化我们的命令,打开 git 安装目录下的 etc/bash.bashrc,在最后一行添加

source /etc/git-completion.bash

对 git-completion.bash 文件进行引用,保存之后再打开 /etc/git-completion.bash ,添加属于自己的自定义指令,例如我配的自定义指令

alias ci='git add . && git commit'
alias project='cd d:/first/second/third/project'

配置好之后重启 git bash ,此时输入 project 就可以直接进入项目路径,提交代码则为

project
ci -m msg

是不是更简短了?我们可以通过在 git-completion.bash 定义各种指令,定制出一套属于自己的指令。

3.3 结合 VS Code

除此之外,我们还可以将自定义指令和 VS Code 的快捷指令结合,例如 VS Code 可以在 git bash 通过 code 命令打开,我们就可以设置自定义指令为

alias project='cd d:/first/second/third/project' && code ./
alias projectpull='cd d:/first/second/third/project' && code ./ && git pull

此时我们输入 project 即可以进入相应项目目录,并在同时在 VS Code 打开了该项目。输入 projectpull 即在前面的基础上对代码进行拉取

除了结合VS Code,还可以和一些全局 npm 包或者 node 脚本一起配合使用。

4. 总结

本文主要从基本概念、常见操作、快捷技巧三个方面对 Git 进行讲解,掌握后基本满足工作需要。但 Git 的使用远不只有这三方面,例如使用 Git 钩子在代码提交前进行检查、项目中 Git 提交信息的规范等,以后将慢慢介绍。

Git 常用命令集

个人使用git的一些总结,一下常用命令的互相搭配使用,能非常完美的处理好日常遇到的99%的情况(不敢说100%,得留点余地)。
还有一些使用频率非常低的指令没有记录,至少普通开发者非常不常用,以后可能会补充。

话不多说
首先需要先下载git这个还是要说一下的

1. 常用命令

1.1 最常用/版本对比/其他

git status 查看当前分支状态
git reflog 查看每一次的命令,都做过什么
git log 查看此分支完整的提交记录,回车继续看,q停止
git log --oneline 查看此分支简略的提交记录,只展示提交号和提交信息
git show 查看最近一次提交的具体代码变化
git show <提交ID> 查看某次提交的具体代码变化
git diff 查看当前代码add后,会add哪些内容
git diff --staged 查看现在commit提交后,会提交哪些内容
git diff HEAD 查看当前代码add并提交后,会提交哪些内容
git log --oneline --graph 图形化展示合并历史

1.2 初始化基本操作

git init 初始化仓库,默认为master分支
git add -A 提交全部文件修改到缓存区
git add <具体某个文件路径+全名> 提交某些文件到缓存区
git commit -m "<注释>" 提交代码到本地仓库,并写提交注释
git commit –a -m "<新注释>" 更改上次提交的注释

1.3 分支操作

git branch 查看本地所有分支
git branch -r 查看远程所有分支
git branch -a 查看本地和远程所有分支
git merge <分支名> 合并分支
git merge --abort 合并分支出现冲突时,取消合并,一切回到合并前的状态
git branch <新分支名> 基于当前分支,新建一个分支
git checkout --orphan <新分支名> 新建一个空分支(会保留之前分支的所有文件)
git branch -D <分支名> 删除本地某个分支
git push <远程库名> :<分支名> 删除远程某个分支
git branch <新分支名称> <提交ID> 从提交历史恢复某个删掉的某个分支
git branch -m <原分支名> <新分支名> 分支更名
git checkout <分支名> 切换到本地某个分支
git checkout <远程库名>/<分支名> 切换到线上某个分支
git checkout -b <新分支名> 把基于当前分支新建分支,并切换为这个分支

1.4 推拉操作(push和pull)

git pull <远程仓库名> <远程分支名> 拉取远程仓库的分支与本地当前分支合并
git pull <远程仓库名> <远程分支名>:<本地分支名> 拉取远程仓库的分支与本地某个分支合并
git push <远程仓库名> <本地分支名> 推送本地某个分支到远程与其对应的分支上,远程没有则会给远程库新建分支
git push <远程仓库名> <本地分支名>:<远程分支名> 推送本地分支到远程某个分支
git push <远程仓库名> :<远程分支名> 删除远程某个分支
git push -f <同上的命令> 强制提交,覆盖线上
git fetch 获取线上最新版信息记录,不合并,用法和pull类似(一个特殊的操作,比如多人开发分支,其他人提交后,自己看分支的时候,可能还知不道线上的分支已经比自己的新了,需要这个指令,来获取一下线上的最新版本,会拉取,但不会合并,存在本地仓库中)

1.5 查看具体文件版本区别(git diff)

git diff <文件名> 对比最近提交,和近近次提交的区别,不加文件名,则为整体对比
git diff HEAD^ -- <文件名> 同上
git diff HEAD~<一个数字> -- <文件名> 上次提交和前第几次提交作对比
git diff <提交ID> <文件名> 上次提交和某次提交作对比
git diff <文件名> 工作区与暂存区比较
git diff HEAD <文件名> 工作区与最近提交比较
git diff <本地分支名> <文件名> 当前分支的文件与其他分支的文件进行比较

1.6 回滚操作

注意,当回滚代码时,resetrevert都可以使用,下面是两个指令的区别
reset:真实硬性回滚,目标版本后面的提交记录全部丢失了
revert:同样回滚,但实际这个回滚操作,算是一个提交,目标版本后面的提交记录也全部都有,而且会多一次提交,就是这次revert

--hard的功能:不加他,文件修改会保留,都会处于add之前的状态;加上他,文件修改会被删除,丢失掉

git <reset/revert> <--hard?> HEAD^ 回退到上次提交
git <reset/revert> <--hard?> <提交记录> 回退到之前的某次提交
git <reset/revert> <--hard?> <某个分支> 回退到此分支的提交状态,相当于复制分支过来
git reset <文件完整路径+完整名> 把add过的某个文件撤销到未add的状态
git reset 把add过的所有文件撤销到未add的状态
git checkout <文件完整路径+完整名> 一个还没有add的文件,撤销修改
git checkout . 还没有add的所有文件,撤销修改

1.7 远程库和分支的链接操作

git remote -v 查看对应的远程仓库地址
git clone <远程仓库地址链接> 克隆某个远程库,默认库名为origin
git clone <远程仓库地址链接> <库名> 克隆某个远程库,并自定库名
git remote remove <远程仓库名> 和远程的某个仓库解除绑定关系
git remote add <远程仓库名> <远程仓库地址链接> 和某个远程仓库建立绑定关系
git push --set-upstream <远程仓库名> <远程分支名> 当前分支和远程某个分支建立绑定关系

1.8 储藏操作(stash)

经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态。
而你想转到其他分支上进行一些工作。
问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。
解决这个问题的办法就是git stash命令。

git stash save <注释信息> 当前分支提交到储藏,插到储藏序列的最前面(不包括新建文件)
git stash save -u <注释信息> 同上,会包括新建文件,功能更强大
git stash list 查看所有储藏中的工作
git stash drop <储藏的名称> 删除对应的某个储藏
git stash pop 取出储藏中最后存入的工作状态进行恢复,会删除储藏
git stash pop <储藏对应的数字> 取出储藏中对应的工作状态进行恢复,会删除储藏
git stash apply 取出储藏中最后存入的工作状态进行恢复,注意,不会删除储藏
git stash apply <储藏的名称> 取出储藏中对应的工作状态进行恢复,注意,不会删除储藏
git stash branch <新分支名> <储藏的名称> 从储藏中新建分支,会删除储藏
git stash clear 清空所有储藏中的工作

1.9 标签操作(tag)

tag 是什么,虽然整个开发过程,每个提交都对应一个提交ID,回滚也比较方便,但总有一些重大意义的提交,比如从v0.91升级到了v1.0,这个v1.0是必须要单独摘出来保存的,否则万一出现回滚事故,导致提交记录丢失呢?
虽然可以单独新建分支保存,但分支的功能并不是用来做这个的。冗余的分支在开发时也容易产生误操作。

所以tag的用处就来了,他可以把某次提交后的项目,整个单独复制拎出来,命名为一个tag(可以以项目版本或打tag的时间来命名),可以写注释,存在tag列表中,与分支隔离开,除非专门命令操作,否则只要项目存在,他们就不会受到影响。

同时,可以使用tag快速回滚项目、基于tag新建分支等,在项目整体的管理上非常方便。

git tag 列出本仓库中所有tag列表
git tag <tag名>' 基于最近的一次提交打tag(轻量tag,不推荐)
git tag -a <tag名> -m '<此tag的注释>' 基于最近的一次提交打tag
git tag -a <tag名> <提交ID> -m '<此tag的注释>' 基于某次提交打tag
git show <tag名> 查看此tag的tag信息
git tag -d <tag名> 删除本地某个tag
git push origin :refs/tags/<tag名> 删除远程仓库中的tag
git push <远程仓库名> <tag名>' 推送某个tag至某个远程库
git push <远程仓库名> --tags' 推送所有tag至某个远程库
git checkout -b <新分支名> <tag名>' 基于某个tag新建分支

2. .gitignore文件

在使用Git的过程中,有的文件比如日志、临时文件、编译的中间文件等不要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交。

没错就是.gitignore文件了。

此文件需要放在.git(默认是隐藏文件,是git的本地仓库)同一目录下

写法如下:

node_modules/
dist/
*.map
npm-debug.log*
yarn-debug.log*
yarn-error.log*

这样,当git add的时候,上面定义忽略的那些文件,就会被git忽略,发生了变化git也不会管。

然而有时候,是会出现意外的。

比如,git已经提交过了,这是后突然说,这里面有个文件需要被忽略,然后我把这个文件写到了.gitignore文件中。。。无效!这个文件仍然没有被忽略!

这是就需要如下命令,来删除此文件并忽略

git rm -r --cached <文件路径+文件全名> 在git库中删除文件并停止追踪,但实际文件还保留,只是忽略掉。

由此,方可。

3. 生成SSH公钥

绑定一个远程仓库,有两种方式:

  • 使用http链接的方式
  • 使用ssh加密链接的方式

如果远程仓库为公开仓库,则两者在使用上,区别不大;
但如果是私密仓库,则http方式,需要输入用户名和密码登录后,才可建立联系,但这样一来,需要把私密仓库的账号和密码暴露,即使添加了项目团队成员,如果成员的账号密码暴露,任何人在任何设备上,登录账号密码,至少都能对项目进行拉取操作,所以就有了下面更合适的方式—ssh

ssh的话,则只需要本地设备,输入一条git指令,生成一对公钥和私钥,然后把公钥的内容,复制添加到远程库的设置中,让远程库认识此设备,就想当于用户填写了账号和密码,好处是,这种方法在远程库的添加上,只认设备不认账户,这样只用保证设备是安全的,仓库就是安全的。

下面为本地设备生成ssh

输入ssh-keygen来生成ssh
window默认存放在C盘/用户/<用户名称>/.ssh文件夹中
Mac默认存放在硬盘/用户/<用户名称>/.ssh文件夹中
其中两个文件,id_rsa.pub为公钥,需要打开复制其中的内容,粘贴到需要的网站中(另一个为私钥,切记保管好)

4. 设置用户名、邮箱、登录账户

git config user.name 查看设置的用户名
git config user.email 查看设置的用户邮箱
git config --global user.name <用户名> 设置用户名
git config --global user.email <邮箱> 设置用户邮箱

修改用户登录信息

  • window

控制面板 -> 用户账户 -> 管理 Windows 凭据,即可看到普通凭据中有git的账户信息,可编辑更改

  • Mac

钥匙串访问.app -> 右上角搜索git,即可看到,可编辑更改

zabbix自定义自动发现服务(low-level-discovery)监控系统服务

一、概述

由于工作关系很久没有更新博客了,本文基于生产配置,是zabbix系列的另一补充;本次要讲的是zabbix Low-level discovery简称(LLD),我们在配置items(监控项)时,有时需要对类似的Items进行添加,换句话说,多台机器上的某一监控具有类似的items,如系统开放的服务,再如磁盘分区,网卡名称等,后两种zabbix已经自带,今天我们以自定义监控每个系统开放的服务来说明 LLD的使用逻辑;

和普通items获取不同的是,LLD 脚本在获取返回时,格式必须是json形式;
和自动发现不同的是,自动发现通过网络发现设备;而LLD是针对主机或模板中用来自动发现定义的items和添加触发器和图形的;

本次测试操作基于zabbix3.4.4 本文中的相关脚本和模板下载

二、LLD脚本

1、获取开放的服务

任何获取items都要基于程序脚本,LLD发现也不例外,以下是获取系统开放服务脚本discovery_services.sh

# cat /usr/local/zabbix-3.4.4/scripts/discovery_services.sh

#!/bin/bash
proarray=($(find /var/run/ -name "*.pid"  2> /dev/null||egrep -v '(rpc|php_daemon|haldaemon|irqbalance|console-kit-daemon)' |awk -F'/' '{print $NF}'|awk -F'.' '{print $1}'))    # 排除不监控的服务

length=${#proarray[@]}
printf "{n"
printf  't'""data":["
printf "t"
printf 'ntt{'
printf ""{#PRO_NAME}":"iptables"}"       #必须要添加的iptables
printf  ","
for ((i=0;i<$length;i++))
do
        printf 'ntt{'
        printf ""{#PRO_NAME}":"${proarray[$i]}"}"
        if [ $i -lt $[$length-1] ];then
                printf ','
        fi
done
printf  "nt]n"
printf "}n"

说明:以上脚本基于 /var/run下的pid进行监控开放的服务,所以如果是公司运维人员自已经开发的管理脚本服务,pid文件请按默认的放到/var/run下 ,这样就不会漏掉,再说大部分系统或知名程序都遵守这一规定,你为什么不能遵守呢?

在一台监控机器上执行如下:

未分类

记住这里的{#PRO_NAME} 这个就是自动发现规则中的宏变量;另外这个脚本返回的是json格式;

2、检查系统状态

对获取的服务进行检查

# cat /usr/local/zabbix-3.4.4/scripts/program_status.sh

#!/bin/bash
procjetName="${1:-NULL}"
LOCK_PATH="/var/lock/subsys"     
RUN_PATH="/var/run"
ret_ok=1
ret_critical=3
ret_unknown=4

if  [[ ${procjetName} == "NULL" ]] ; then
        echo ${ret_unknown}
fi

if [ -f "${LOCK_PATH}/${procjetName}" ] || [ -f "${RUN_PATH}/${procjetName}.pid" ]  || [ -f "${RUN_PATH}/${procjetName}/${procjetName}.pid" ] ; then
        echo ${ret_ok}
else
        echo ${ret_critical}
fi

以上脚本检查如果服务存在则返回1 否则返回 3 ,如果服务不存在则返回4
检查的原则就是在/var/run/下是否有和服务同名的pid文件存在,或/var/run/服务/服务.pid存在,对于像iptabls这种则检查 /var/run/subsys/服务.pid 三种情况满足一种即可;

运行效果如下:

未分类

对iptables服务检查 返回 1说明 iptables正常

关闭iptables 再次进行检查:

未分类

而对httpd服务进行检查,由于本机根本没有httpd服务,所以返回的是3

未分类

3、定义items

# cat /usr/local/zabbix-3.4.4/etc/zabbix_agentd.conf.d/LLD_Services.conf
UserParameter=services.scan,/bin/bash /usr/local/zabbix-3.4.4/scripts/discovery_services.sh
UserParameter=services.status[*],/bin/bash /usr/local/zabbix-3.4.4/scripts/program_status.sh $1

三、添加模板和自动发现规则

这里需要在web页上添加,和添加items很类似;

1、创建模板

进入web端;单击 配置–模板–配置模板–创建模板– 这里模板名叫 “Ickey Services status” –创建应用集(“Services_status”) 如图:

未分类

未分类

2、创建自动发现规则(LLD)

如上图点自动发现规则在模板中创建 自动发现规则 — 创建自动发现规则 如图:

未分类

这里需要注意的是 键值: servies.scan 即item 是在2.3小节的配置文件中定义的,不可乱写

再配置上图中的过滤器 配置变量(宏)如图:

未分类

此处的{#PRO_NAME}就是上面2.1节中脚本返回的变量; — 保存

3、创建 配置 监控项原型

如图:

未分类

填写名称,等相关信息如图:

未分类

此处需要注意的$1 和键值 是 2.3 定义items中的$1 也即是服务名; 保存;

4、创建触发器类型

如图:

未分类

未分类

说明:触发器名称:即是 “服务名” is down

表达式的意思就是 当发现某服务返回的值不是 1时发出警告

保存 这个 模板中的自动发现规则 就完成啦

但是这里存在一个问题,当我们机器上yum工具时,会在/var/run下生成yum.pid这样就会侦测到一个新服务,yum,但yum执行完后yum.pid会被删除,此时触发器就会报yum is down,为了解决这类问题,我们可以修改触发器报警策略,修改成如下:

未分类

意思就是,最后状态不是1 同时一小时前的状态是1;这样那此在/var/run生成临时pid的程序就不会被触发;

接下来就是在主机上应用模板,可以批量添加;这里就忽略了;

5、测试

看下效果图:

未分类

未分类

可以看到此主机应用了模板后已经可以拿到监控项数据了,我们来关闭这台主机的防火墙服务试试,看看触发器是否正常;

如图:

未分类

再把iptables服务启动

未分类

可以看到服务关闭和启动可以报警与恢复;至此自动发现服务已经可以正常使用啦!通过这个实例,希望大家能理解自动发现规则的原理逻辑!本文基于生产故有些截图带有涂;主要用于备忘与分享,如有错误之处欢迎留言!

redhat 6.5 安装和配置zabbix客户端

一、安装zabbix-agent端

rpm -ivh http://repo.zabbix.com/zabbix/2.4/rhel/6/x86_64/zabbix-release-2.4-1.el6.noarch.rpm
yum install zabbix-agent-2.4.6-1.el6.x86_64

二、配置agent端配置文件

配置agent端配置文件

vim /etc/zabbix/zabbix_agentd.conf       #此处千万别写成了zabbix_agent.conf,否则配置了不生效
Server=115.29.245.240                    #填写Server的IP地址
ServerActive=115.29.245.240              #修改为Server的IP地址
Hostname=115.29.245.240                   #填写Server的HostName,注意Server端要能解析
UnsafeUserParameters=1                   #是否允许自定义的key,1为允许,0为不允许
Include= vi etc/zabbix/zabbix_agentd.conf.d/  #自定义的agentd配置文件(key)可以在这里面写

如果该文件中的内容(安装目录和可执行文件目录)与实际状况不同,那么则需要修改

三、启动Zabbix Agent

启动Zabbix Agent服务

service zabbix-agentd_ctl start

四、设置开机启动

设置Zabbix-Agent服务自启动

chkconfig zabbix-agent on

利用zabbix生成awstats日志分析图表并用Python调用zabbix API批量添加item

awstats作为一款日志分析软件,功能不错,但是界面过于简单,也没有图表功能,这里我采取了一种变通的方法,将awstats的分析结果(pv、hits(文件数)、bandwidth、visits(独立ip))添加到zabbix,并通过zabbix生成趋势图表。

在前两篇文章中,我们队awstats的使用及其工作方式进行了简明扼要的介绍:awstats对每个站点进行分析之后,会生成一个“awstats012016.txt”格式的“数据库”文件;awstats的展示页面便是从该文件中取数据生成的。

  1. 多server多站点情况下awstats日志分析:http://kaifly.blog.51cto.com/3209616/1719248

  2. awstats CGI模式下动态生成页面缓慢的改进:http://blog.51cto.com/kaifly/1770137

这篇文章的思路就是从这个文本格式的‘数据库文件’中取得我们想要的数据,然后通过自定义的脚本将其添加到zabbix中,最终满足我们生成pv趋势图表的需求。

而完成此任务的关键就是分析似‘awstats052016.txt’的数据文件的内容格式(ps:以笔者“多年”shell经验来看,”分析源文件格式“和“生成目标文件格式”这俩“格式”在日常的shell编程中占用了很大一部分时间。扯远了O(∩_∩)O~)

首先是自定义脚本作为zabbix的key,从对应的‘数据文件’中取得pv、hits、bandwidth、visits的值。用shell实现如下

cat web_pv.sh

#!/bin/sh
#从例如api/awstats052016.txt这样的awstats数据库文件里取得昨天的pv等统计(因为awstats本身就是统计到昨天的日志)
#by  ljk  20160506
#blog    http://kaifly.blog.51cto.com/

#shell脚本的$1 $2分别代表站点名称(格式如www或bbs)和统计项(pv 文件数 字节 独立ip)

basedir='/usr/local/awstats-7.4/result'
date_f1=$(date +%m%Y -d '1 day ago')
date_f2=$(date +%Y%m%d -d '1 day ago')

cd $basedir/$1
#下面关于awk的用法中有一个小技巧,匹配到指定的项之后,停止继续搜索余下的内容。这对于体积较大的文件可以节约不少时间
content=`awk '$1 == "'$date_f2'" {{print} {exit}}' awstats$date_f1.txt`
case $2 in
    "pages")
        echo $content|awk '{print $2}';;    #pv
    "hits")
        echo $content|awk '{print $3}';;    #hits/文件数
    "bandwidth")
        echo $content|awk '{print $4}';;    #bandwidth/字节
    "visits")
        echo $content|awk '{print $5}';;    #visits/独立ip
    *)
        echo "unknow value";;
esac

然后在awstats所在的server的zabbix的‘userparameter.conf’文件中添用户自定义key,并重启zabbix_agentd

UserParameter=web_pv[*],/bin/sh /usr/local/zabbix/etc/zabbix_agentd.conf.d/web_pv.sh $1 $2

接着在zabbix_server端通过zabbix_get命令尝试获取这些值,key格式为“web_pv[站点名,监控项]”,例如

未分类

能取到值,说明自定义key是ok的。

接下来就是在zabbix里添加各站点的item了,这里通过Python实现(zbx接口通过json传递数据,处理json python比shell方便太多了)

这里需要仔细阅读下zabbix的api文档https://www.zabbix.com/documentation/3.0/manual/api和查看zabbix数据库结构(确保万无一失嘛)

首先在zabbix里创建一个template,名为Template site PV,这一步手动创建即可

然后开始通过Python自动化添加items

cat shells/add_web-pv_item_to_zabbix.py

#!/bin/env python3
"""
将各站点的4种(pages,hits,bandwidth,visits)item添加/更新到zabbix的 'Template Site-PV'
by ljk  20160507
"""

import os,requests

basedir='/usr/local/services/awstats-7.4/result/'
items=['pages','hits','bandwidth','visits']

url='http://192.168.1.199/api_jsonrpc.php'
zbx_api_headers={'Content-Type':'application/json-rpc'}    #定义通过zabbix api操作时必带的header

#取得用于zabbix api认证的token,通过用户名密码请求api生成
#生成方式请参考api文档,有个这个token,可以省去账号密码认证
api_auth="738024dfda33cc6020fb1f5e3617" 

#这里我在前期实验的时候,手动添加了几个item了,所以这里先取出template里已经存在的item,以便后期创建时过滤掉这些item
exist_items_filter={    #通过zabbix api查询已经存在的web_pv[*,*]的item,这里是json格式的过滤条件
    "jsonrpc": "2.0",
    "method": "item.get",
    "params": {
        "output":[
            "name",
        ],
        "search": {
            "key_":"web_pv"
        }
    },
    "auth":api_auth,
    "id": 0
}
exist_items=requests.post(url,headers=zbx_api_headers,json=exist_items_filter)

os.chdir(basedir)
sites=os.listdir(path='.')

def create_item():
    for site in sites:
        for item in items:
            if site+'-'+item not in exist_items.text:
                #先给不同情况下的units和multiplier赋值
                if item=='pages' or item=='hits':
                    units='万'
                    multiplier=0.0001
                elif item=='bandwidth':
                    units='B'
                    multiplier=1
                else:
                    units=''
                    multiplier=1
                #定义创建item的json数据格式
                num=10
                create_item_post={
                    "jsonrpc": "2.0",
                    "method": "item.create",
                    "params": {
                        "name": site+','+item,
                        "key_": "www_pv["+site+','+item+"]",
                        "hostid": "10134",
                        "type": 0,
                        "value_type": 3,
                        "history": 7,
                        "delay": 28800,
                        "units": units,
                        "applications": [774],
                        "interfaceid": "0",
                        "formula": multiplier
                    },
                    "auth": api_auth,
                    "id": num
                }
                try:

                    create_item_result=requests.post(url,headers=zbx_api_headers,json=create_item_post)            
                    #打印处理每个条目的结果
                    print('{}-{}: return_code {} details {}'.format(site,item,create_item_result.status_code,create_item_result.json))
                    num+=1
                except:
                    print('{}-{}: error'.format(site,item))
                    import sys
                    sys.exit(255)

#create_item()

#update函数,其实是我在执行create_item()的时候将key的名字写错了,无奈在写一个update_item()吧
def update_item():
    num=100    #对应zbx api中的id字段,随意指定,确保每次调用api时该值不同即可(这里用自增的方式)

    #定义更新item的json数据格式
    update={
        "jsonrpc": "2.0",
        "method": "item.update",
        "params": {
            "itemid": "",
            "key_": ""
        },
        "auth": api_auth,
        "id": num
    }

    #取得site pv模板下所有错误的item(key以www_py开头的),hostid的值实际为template site PV模板的templateid
    wrong_items_filter={
            "jsonrpc": "2.0",
            "method": "item.get",
            "params": {
               "output":["key_","hostid"],
                "search": {"hostid":"10134","key_":"www_pv"}
            },
            "auth": api_auth,
            "id": 0
        }

    wrong_items=requests.post(url,headers=zbx_api_headers,json=wrong_items_filter).json()['result']    #wrong_items为list

    for wrong_item in wrong_items:
        if wrong_item['hostid'] != '10119':    #img2从template site pv继承而来 所以这里每个item对应两条记录对应template site pv的hostid:10134和img2的hostid:10119,所以不需要修改img2的
            update['params']['itemid']=wrong_item['itemid']
            update['params']['key_']=wrong_item['key_'].replace('www','web',1)            
            try:
                update_item_result=requests.post(url,headers=zbx_api_headers,json=update)
                print('{} ---- details {}'.format(wrong_item['key_'],update_item_result.json()))
                num+=1
            except:
                print('{}-{}: error'.format(site,item))
                import sys
                sys.exit(255)
#update_item()

后续的批量生成image和生成screen都可以通过zbx 的API来完成,这里就不再列举了

ok,最后看两张zabbix生成的靓图吧

未分类

未分类

WordPress密码找回或修改

自己的Wordpress出现了两次密码忘记的情况,第一次是浏览器Chrome有记忆密码可以进入控制面板的情况,另一次手贱改了密码直接控制面板都进不去了,网上找了各种介绍,感觉不是很系统,乱七八糟,对于一个我类似的小白都把简单问题说的复杂化了,总结一下我的方法吧,希望对大家有用。

WordPress我目前用的4.99版本,在忘记密码后,控制台点忘记密码,发送的邮件无法修改密码,据说是Wordpress的问题,几年了还没改。所以不要指望这个方式改密码了。网上有通过修改页面的语句的,但是我没有成功,后来也觉得比下面方式麻烦风险也更大些。

未分类

一. 浏览器还记忆着wordpress控制面板密码的情况,能进入控制台。

这种情况很好解决,进入控制台。也就是进入:你的网址/wp-admin/

进入后找到左侧的-用户

未分类

然后左侧中央就会出现自己的wordpress的账户了,点击”编辑”,英文版本就是”Edit”,话说英语好学编程都容易,难怪当初我那时候高考招生软件的有英语成绩要求,还是外教授课英文教材,学费也是吓退一群人,那还是软件没火的年代。。。

未分类

然后下拉菜单,找到”新密码-生成密码”,点击生成密码,

未分类

点击后会随机生成一个密码,删除它,修改成自己想要的密码,然后点击左下角的”更新个人资料”,就完成了更新密码。别忘了把浏览器的记忆密码改成这个密码,这样就算忘记了密码,只要能默认进入控制台还是能随便改密码的。

未分类

二. 浏览器默认密码不能直接进入控制面板了。。。我第二次就是,通过上面方式更新了密码后没有及时修改chrome浏览器的默认进入密码,大家记得及时修改。或者用小本本记下wordpress密码,毕竟这东西不是银行卡密码,记本上也没事的。

这种修改其实也很简单,但是查了很多网址说的不是很清楚,搞得晕头转向好像很复杂。方式就是通过数据直接修改,用到的软件是PhpMyAdmin,又称php。

刚开始看了好多文章就直接说用这个软件登录数据库,尼玛都不说怎么安装的,看了一堆教材怎么在自己服务器安装,最后才发现如果一般人们用的LNMP一键安装安装包就默认安装了这个软件。。。哎。这下更觉得LNMP这个傻瓜安装包真的想的挺周到的。支持!!!

登录方式也很简单:直接输入自己的网址的iP地址进入LNMP页面,别告诉IP地址我这个你也能忘,快去查查吧。通过这个地方我终于知道我的DNS带免费的反查功能,也就是通过leiyin.info反查IP地址是多么重要了,可以保护自己的服务器安全啊。

未分类

点击PhpMyAdmin进入软件。这时要求输入用户名和密码,1.5之前版本如果安装时不输入直接回车,密码为root,否则为你输入的密码。1.5及之后版本为 lnmp.org#随机数字。如果又忘记了密码也可以恢复默认,方法可以在LNMP的网站找方法,地址如下https://lnmp.org/faq/lnmp-1-2-tools.html

未分类

这时候我无法登录PhpMyAdmin,以为悲剧了,可是明明记录了密码啊。后来又是网上各种找,不过后来想起来不会和浏览器有关吧,换上IE直接试试,顺利进入。。。好吧

进入后找到wp_users这个语句,点击进入。

未分类

点击Edit进行编辑。

未分类

在user_pass中,把varchar(255)改成MD5,后面设置成123456,也就是登录密码改成了123456。

这里varchar(255)有的不是255,无所谓的。

未分类

点击最下端的”Go”完成修改。然后可以使用123456的密码登录wordpress控制面板了。记得登陆后把密码123456用第一种方法修改成别的,然后记录在小本本上。

未分类

第二步参考了下面网址,表示感谢https://www.bwgyhw.com/bandwagonhost-build-page-phpmyadmin/

大功告成。又学会了一些网站的假设和使用,虽然很简单吧,还是很有成就感的。越发觉得LNMP挺好的,下一步准备升级1.5版本,然后把网站改成加密的,涉及到wordpress的备份恢复了,等我的帖子吧。