2009年9月

Git的权限控制

Git不像SVN或者CVS那样,有明确的一套权限控制方法
在Git中想要实施对仓库的版本控制,大概可以有以下2个办法:

1.Git Hook:


Hooks are little scripts you can place in $GIT_DIR/hooks directory to trigger action at certain points. When git-init is run, a handful of example hooks are copied into the hooks directory of the new repository, but by default they are all disabled. To enable a hook, rename it by removing its .sample suffix


git默认提供了很多类型的hooks,比如pre-commit,post-commit,post-checkout,update,pre-receive....
我们可以在update的hooks中进行控制,当用户在local repository上执行git push,在remote repository更新之前,会调用该hook
该hook存放在$GIT_DIR/hooks/update下

可参考http://www.kernel.org/pub/software/scm/git/docs/howto/update-hook-example.txt

以下是update脚本内容,本人没有测试过是否可用


-- >8 -- beginning of script -- >8 --

#!/bin/bash

umask 002

# If you are having trouble with this access control hook script
# you can try setting this to true.  It will tell you exactly
# why a user is being allowed/denied access.

verbose=false

# Default shell globbing messes things up downstream
GLOBIGNORE=*

function grant {
  $verbose && echo >&2 "-Grant-    $1"
  echo grant
  exit 0
}

function deny {
  $verbose && echo >&2 "-Deny-    $1"
  echo deny
  exit 1
}

function info {
  $verbose && echo >&2 "-Info-    $1"
}

# Implement generic branch and tag policies.
# - Tags should not be updated once created.
# - Branches should only be fast-forwarded unless their pattern starts with '+'
case "$1" in
  refs/tags/*)
    git rev-parse --verify -q "$1" &&
    deny >/dev/null "You can't overwrite an existing tag"
    ;;
  refs/heads/*)
    # No rebasing or rewinding
    if expr "$2" : '0*$' >/dev/null; then
      info "The branch '$1' is new..."
    else
      # updating -- make sure it is a fast forward
      mb=$(git-merge-base "$2" "$3")
      case "$mb,$2" in
        "$2,$mb") info "Update is fast-forward" ;;
  *)    noff=y; info "This is not a fast-forward update.";;
      esac
    fi
    ;;
  *)
    deny >/dev/null \
    "Branch is not under refs/heads or refs/tags.  What are you trying to do?"
    ;;
esac

# Implement per-branch controls based on username
allowed_users_file=$GIT_DIR/info/allowed-users
username=$(id -u -n)
info "The user is: '$username'"

if test -f "$allowed_users_file"
then
  rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
    while read heads user_patterns
    do
      # does this rule apply to us?
      head_pattern=${heads#+}
      matchlen=$(expr "$1" : "${head_pattern#+}")
      test "$matchlen" = ${#1} || continue

      # if non-ff, $heads must be with the '+' prefix
      test -n "$noff" &&
      test "$head_pattern" = "$heads" && continue

      info "Found matching head pattern: '$head_pattern'"
      for user_pattern in $user_patterns; do
  info "Checking user: '$username' against pattern: '$user_pattern'"
  matchlen=$(expr "$username" : "$user_pattern")
  if test "$matchlen" = "${#username}"
  then
    grant "Allowing user: '$username' with pattern: '$user_pattern'"
  fi
      done
      deny "The user is not in the access list for this branch"
    done
  )
  case "$rc" in
    grant) grant >/dev/null "Granting access based on $allowed_users_file" ;;
    deny)  deny  >/dev/null "Denying  access based on $allowed_users_file" ;;
    *) ;;
  esac
fi

allowed_groups_file=$GIT_DIR/info/allowed-groups
groups=$(id -G -n)
info "The user belongs to the following groups:"
info "'$groups'"

if test -f "$allowed_groups_file"
then
  rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
    while read heads group_patterns
    do
      # does this rule apply to us?
      head_pattern=${heads#+}
      matchlen=$(expr "$1" : "${head_pattern#+}")
      test "$matchlen" = ${#1} || continue

      # if non-ff, $heads must be with the '+' prefix
      test -n "$noff" &&
      test "$head_pattern" = "$heads" && continue

      info "Found matching head pattern: '$head_pattern'"
      for group_pattern in $group_patterns; do
  for groupname in $groups; do
    info "Checking group: '$groupname' against pattern: '$group_pattern'"
    matchlen=$(expr "$groupname" : "$group_pattern")
    if test "$matchlen" = "${#groupname}"
    then
      grant "Allowing group: '$groupname' with pattern: '$group_pattern'"
    fi
        done
      done
      deny "None of the user's groups are in the access list for this branch"
    done
  )
  case "$rc" in
    grant) grant >/dev/null "Granting access based on $allowed_groups_file" ;;
    deny)  deny  >/dev/null "Denying  access based on $allowed_groups_file" ;;
    *) ;;
  esac
fi

deny >/dev/null "There are no more rules to check.  Denying access"

-- >8 -- end of script -- >8 --
This uses two files, $GIT_DIR/info/allowed-users and
allowed-groups, to describe which heads can be pushed into by
whom.  The format of each file would look like this:

        refs/heads/master  
;junio
  +refs/heads/pu    junio
        refs/heads/cogito$  pasky
        refs/heads/bw/.*  linus
        refs/heads/tmp/.*  .*
        refs/tags/v[0-9].*  junio

With this, Linus can push or create "bw/penguin" or "bw/zebra"
or "bw/panda" branches, Pasky can do only "cogito", and JC can
do master and pu branches and make versioned tags.  And anybody
can do tmp/blah branches. The '+' sign at the pu record means
that JC can make non-fast-forward pushes on it.


注意:最后需要配置$GIT_DIR/info/allowed-users和 $GIT_DIR/info/allowed-groups这2个文件,里面定义了每个user对应的可操作的分支、标签。

采用hook的方式还是比较简陋的,它要求在git服务器上为每个需要控制的用户都建立一个真实的用户,操作起来比较麻烦,而且还会衍生该用户其它的权限问题,对于一个大的团队来说,并不太合适。

2.Gitosis:

参见http://www.tech126.com/?p=60/

使用Gitosis来管理权限,相对于hook方式来说,省掉了建立n多用户的麻烦,但它是通过ssh来连接git服务器的,所以需要每个用户生成对应的公钥文件,并提交到Gitosis-admin中。

注意:Gitosis的权限控制本质上是针对每个Client的,而不是针对每个user,所以使用它并不能校验user.name是否合法,它只会校验Client是否有权限,对于大家都在一个公共机器上做开发,一定要每个用户都必须git config user.name xxxx。

升级远程服务器的SSH

想把某台远程服务器从ssh1升级到ssh2

1. 升级zlib版本到1.2.3

2. 安装openssh

3.在服务器上把ssh2启动到另外一个端口

/usr/sbin/sshd -p 8081

如果启动出如下错误:

 

 

Could not load host key: /etc/ssh/ssh_host_key

Could not load host key: /etc/ssh/ssh_host_rsa_key

Could not load host key: /etc/ssh/ssh_host_dsa_key

Disabling protocol version 1. Could not load host key

Disabling protocol version 2. Could not load host key

sshd: no hostkeys available -- exiting.

则执行:

ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key

 

4.使用ssh xxxx -p 8081连接到远程服务器,杀掉22端口,并启动新的22端口

/usr/sbin/sshd

5.然后使用ssh的22端口连接到远程服务器上,杀掉8081端口即可

使用Gitosis管理git仓库

Git作为一个分布式的版本控制系统
功能虽然强大,但其在用户权限管理方面比较弱
使用Gitosis,可以方便的给git的某个仓库添加对应的用户和权限
以下记录一下安装和配置的过程:

1.保证服务器上有python和setuptools包

2.获取并安装gitosis


cd gitosis
git clone git://eagain.net/gitosis.git
python setup.py instll


3.新建一个git用户


useradd git


4.在需要管理gitosis的Client机器上,生成rsa公钥


ssh-keygen -t rsa
mv id_rsa.pub ysz.pub
scp ysz.pub xxxxx:~/.ssh/


5.初始化gitosis


sudo -H -u git gitosis-init < ~/.ssh/ysz.pub
chmod 755 /home/git/repositories/gitosis-admin.git/hooks/post-update


6.Client端配置gitosis

git clone git@192.168.95.26:gitosis-admin.git


注意:如果Gitosis管理员在clone时也出现read access denied
可以直接到gitosis的目录上去修改对应的配置文件
路径一般在/home/git/repositories/gitosis-admin.git/gitosis.conf

一个gitosis.conf的例子


[gitosis]
loglevel = DEBUG

[group gitosis-admin]
writable = gitosis-admin
members = ysz

[group mailcenter]
writable = gitosis_test gitosis_test2
members = ysz baboy

[group mailcenter_ro]
readonly = gitosis_test
members = ysz_pc

[repo gitosis_test2]
description = gitosis test2 project
owner = ysz


这样,ysz和baboy对gitosis_test和gitosis_test2仓库都有读写权限
但是,ysz_pc只对gitosis_test只有读的权限,也即只能clone和pull,不能push
注意:当增加新用户时,需要把对应的userid.pub文件给拷贝到keydir目录下,然后push上去就可以了
然后,对应的userid就可以

git clone git@xxxxx:gitosis-test.git


7.如果需要设置一个仓库为公共仓库,不需要rsa公钥即可访问
首先启动git daemon

sudo -u git git daemon --base-path=/home/git/repositories/


然后到要共享的仓库目录下

touch git-daemon-export-ok


这样,任何人都可以

git clone git://xxxxx/gitosis_test2


注意,通过git协议来clone的仓库,默认是没有push权限的
如果要开通匿名的push权限,可以加一个--enable=receive-pack参数

sudo -u git git daemon --enable=receive-pack --base-path=/home/git/repositories/


如果要以守护进程的方式运行git-daemon,可编辑/etc/xinetd.d/git

service git
{
disable = no
socket_type = stream
wait = no
user = git
group = git
server = /usr/local/libexec/git-core/git-daemon
server_args = --inetd --syslog --verbose --base-path=/home/git/repositories/ --enable=receive-pack
}


然后,再重启xinetd

service xinetd restart

Git: how remotes work(zz)

One of the difficult things for a git beginner to understand is how remote branches work.

Basically, as git is a distributed version control system, every developer has a full and independent repository. So, how can you pass changes around?

In the examples below, we'll consider a remote repository, that we'll call origin, and a local one (that we'll call local). The remote repository has one branch, called master, that has been cloned as origin/master on the local repository. Moreover, the local repository has one local branch, called master as well (but it doesn't need to be), which is set up to track changes that happen on origin/master. Note that, as origin/master is a remote branch, it cannot be checked out -- only master can.

The fetch operation (command git fetch) copies the latest commits from the master on origin to origin/master, and updates the HEAD of the origin/master branch:

git fetch origin

The circles on the schema (click for a larger version) represent commits, and the arrows are the parent->child relationship between commits. The labels indicate the various HEADs (or branches). It is to be noted that a branch is nothing more than a label following the HEAD of a series of commits.

At the end of this operation, origin/master matches the master branch on the origin, but master on the local repository is still behind. We need to use the git merge command to make master point at the same commit than origin/master:

git merge origin/master

This kind of merge is called a fast-forward because no actual merging of changes is involved. No new commit is created; we have just moved a HEAD forward in history. And that's fast.

Now, what happens if you committed a change on your master on local? Nothing changes for the fetch; the two new commits are still created from origin's master so origin/master matches it exactly:
git fetch origin

However, on local, master and origin/master have bifurcated. To reunite them, you'll need to use git merge, that will create another commit, and make master point to it:
git merge origin/master

The new commit (in orange) is a merge commit: it has two parents. (If conflicts happens, git will ask you to resolve them.)

Ah, but now, your master has two more commits that the origin's master. And you surely want to share your changes with your fellow developers. That's where the git push command is useful:
git push origin

git push will start by copying your two commits to the origin, and ask it to update its master to point at the same location than yours. At the end of the operation, both commit trees should match exactly. Note that git push will refuse to push if your origin/master is not up to date.

The "origin" argument to git fetch and git push is optional; git will use all remotes if you don't specify one.

Finally, a note: as fetch and merge are often done together, a git command combines both: git pull. It's smarter than the addition of the two commands, because it knows how to look in your git config what remote branch is actually tracked by your current local branch, and merge from there -- so you don't even need to type the name of origin/master for the merge.

git中的origin/master

在使用git时
如果我们使用了clone或者git push到远程的某个origin上后
使用命令

git branch -a


会发现,有一个origin/master的分支

这实际上是git从远程clone下来的一个分支
origin/master指向远端origin的master分支,用来跟踪远程origin的master变化情况
只有git再和远程的origin通讯(比如fetch,pull)后,origin/master分支的指针才会更新移动

当我们使用

git fetch


或者

git remote update


时,就会把远端的代码给更新到origin/master上
此时,如果我们想以远程分支为基础,建一个本地分支来做开发,那么

git checkout -b local_branch origin/master


所以,当合并的时候,需要

git merge origin/master




之前讲过,Git往远程push前修改commit信息
使用的是

git rebase -i origin/master


也就不难理解了
它rebase的是origin/master的commit信息
所以push之后,远端的commit信息就会改变的

推荐一篇文章
http://consttype.blogspot.com/2008/10/git-how-remotes-work.html
不过似乎被和谐了,得用代理才能访问
你也可以访问这个
http://www.tech126.com/?p=59/

使用自增ID作为表的主键

之前,DBA就曾和我说过
最好用一个没有意义的自增长id来作为表的主键
当时也没有怎么去想这么做的意义

直到最近
我们的Sqlite出了一个这样的问题
当Sqlite库出现“database disk image is malformed”错误时
我们采用了Sqlite数据库的恢复这样的办法来恢复
按常理来讲,应该是没问题的

可惜,我们的某个表中使用了Sqlite自身默认的rowid来作为主键
而且,该rowid还和另外一个表的某个字段进行了关联
所以,dump出来的数据再导进去后,某些行的rowid就发生了变化
这样,和另外一个表的对应关系就断掉了.....
无奈,只能通过查询坏掉的sqlite对应记录来来修改对应的rowid
或者,通过很麻烦的办法来重建Sqlite,再去修改对应的status
如果,当初我们使用一个自增长的id来作为主键并关联的话
那么,就不会有这个问题了......


血的教训啊......

Post方式的Http请求

在使用python的httplib2后台发送http请求时
如果采用get方式
一般不会有什么问题,因为get本身不需要什么额外的header
但如果采用post方式
有时候经常会忘掉Content-Type和Content-Length这2个headers
结果会造成无法成功请求

对于Content-Type:
如果发送的是name-value pairs类型的数据,则指定application/x-www-form-urlencoded
如果发送的是二进制的数据,则指定application/octet-stream

当然,如果在java里,我们使用了httpclient来发送的话
Content-Type是不需要我们手工指定的
但Content-Length最好还是指定一下

Linux下“参数列表过长”的问题

当某个文件夹下的文件数目过多时
如果执行cp,mv,tar......等命令时
会提示“-bash: /bin/tar: 参数列表过长”的错误
这个时候,就用find吧

find . -exec cp {} ../Maildir/test/cur/ \;

TC快捷键

自己定义的TC快捷键列表

F1=cm_MkDir(新建文件夹)    
F2=cm_RenameSingleFile(重命名文件)
CTRL+H=cm_SwitchHidSys(是否显示隐藏文件)
CTRL+1=cm_CopyFullNamesToClip(拷贝完整路径和文件名)
CTRL+2=cm_CopySrcPathToClip(拷贝完整路径)
CTRL+3=cm_CopyNamesToClip(拷贝文件名)
CTRL+F=cm_SearchFor(查找)

以下是系统默认的
CTRL+G=cm_ExecuteDOS(打开DOS窗口)
CTRL+D=Open directory hotlist ('bookmarks')(打开常用文件夹)
CTRL+M=Multi-Rename-Tool(批量重命名)
F5                     Copy files(复制文件)
F6                     Rename files(移动文件)
CTRL+\                 Jump to the root directory (跳到根目录)
CTRL+F3                Sort by name(按名字排序)
CTRL+F4                Sort by extension(按扩展名排序)
CTRL+F5                Sort by date/time(按日期排序)
CTRL+F6                Sort by size(按大小排序)
CTRL+tab               Jump to next tab(切换到下一个标签)

关于家用dvd的usb接口(转)

    一般来说DVD播放机容许USB接口输入的文件类型与从光盘读入的文件类型是相同的。DVD向下兼容CD;VCD;MP3等多种音视频媒体格式。也兼容采用MPEG4编码的流媒体格式。这就极大地丰富了节目的来源。我们不需要先把视频刻成光盘就能直接播放。降低了使用的成本。当DVD播放USB上面的内容时。不需要光盘驱动电机的高速旋转,也不需要激光头工作。机器内内无机械磨损。还能延长机器的使用寿命减少了故障率。
    虽然USB接口兼容的媒体格式比较多,但并不是所有的兼容格式都适合使用USB接口播放。常用USB接口的类型有USB1.1和USB2.0;后者的读写速度大于前者。
    所播放媒体高的清晰度和流畅度需要有有充足的数据流量的支持。如果数据的读写速度跟不上播放的速度需求。画面就会出现短暂的停顿,(俗称“卡”)。这个时侯就要想办法减少数据流量使数据得以缓冲。因此要对视频进行压缩,压缩视频是要以牺牲画质为代价的。
    每分钟DVD的视频大约需要43兆的数据量。一分钟VCD的视频大约需要10兆的数据量。由于受USB接口读写速度的限制和储存介质工作速度的限制。虽然理论上可以播放DVD的VOB格式的视频文件 ,可实际上很难有几部机器能够流畅播放的。这是因为VOB的文件太大了。可能超出了USB的读取能力。VCD 的DAT文件的确可以流畅播放。可我们又对这种画质不太满意。因此适合播放的文件类型也只有MPEG4的AVI了。一个参数配置的合理的AVI视频文件,画质可以接近DVD;而文件大小又接近VCD。虽然在压缩率方面比不过RMVB,但在RMVB尚未完全普及到家用dvd播放机上时。AVI不失为一个良好的选择。

    AVI作为音视频的容器。其种类繁多,各种类之间相互并非完全兼容。实际操作时应根据该产品的使用说明书而定。这里仅以普通而兼容较好的AVI格式分为几个方面进行简单的介绍。

    文件大小:我们拿一张普通的DVD电影光盘,把它放进光驱并使用资源管理器打开。你会发现视频文件夹内的VOB文件最大不会超过0.99G;这是因为DVD机的播放程序只能识别小于1G的文件。所以在转换制作AVI电影文件时。如果大于1G。一定要剪开。不然影片的后半段可能会无法播放出来。
    视频编码:为了使转出来的AVI文件有较好的兼容性,请选用DIVX或 XVID编码器对视频进行编码。
    音频编码:为了避免转出来的电影播放时有图像无声音。音频要选用MP3编码。这是因为DVD都能播放MP3格式的声音文件。
    图像分辨率:标准DVD播放机的内置解码器对图像分辨率的处理能力大概是720×576的DVD画质水平。高于这个像素的部分高清AVI文件,普通的DVD播放机可能无法播放。
    文件名:DVD播放机无内置汉字库。不能正确显示中文文件名(显示乱码)。为了便于识别和选择播放,最好把文件名改为字母或数字。

    储存AVI文件最好选择读写速度高的U盘或SD卡(读卡器)
    最后提醒大家一点,千万不要突然拔下在正播放中的U盘。一定要在按了停止键以后

最新文章

最近回复

  • feifei435:这两个URI实际是不一样的
  • zsy: git push origin 分支 -f 给力!
  • 冼敏兵:简单易懂,good fit
  • Jack:无需改配置文件,看着累! # gluster volume se...
  • Mr.j:按照你的方法凑效了,折腾死了。。。。
  • zheyemaster:补充一句:我的网站路径:D:\wamp\www ~~菜鸟站长, ...
  • zheyemaster:wamp2.5(apache2.4.9)下局域网访问403错误的...
  • Git中pull对比fetch和merge | 炼似春秋:[…] 首先,我搜索了git pull和git fe...
  • higkoo:总结一下吧, 性能调优示例: gluster volume s...
  • knowaeap:请问一下博主,你维护的openyoudao支持opensuse吗

分类

归档

其它