Showing posts with label git. Show all posts
Showing posts with label git. Show all posts

Wednesday, May 21, 2014

[git] 建立以及使用Git的bare repo

git repo分为bare和non-bare两种. non-bare repo最最常见, 它包括一个working tree(你提交到这个git repo里的文件和目录结构)以及一个.git文件夹(其中有各种git系统文件). 而bare repo仅仅有系统文件. 一般在git server上,所有的repo都是以bare形式存在的. 通常除了admin以外,大家是不需要和bare repo打交道的.

不过事事无绝对, 我就在工作中遇到这样的一个问题需要和bare repo打交道: 我的所有源代码都放在一个远端的git的repo(比如放在github上), 可是由于某些缘故我或者不能或者不愿意在我工作环境里直接和github上的repo同步. 一个折衷的方案就是在一台自己的server上建立这个github上的repo, 然后在我的工作环境中首先和server上得 repo同步, 然后再登陆到server上把server里地repo和github上的repo同步.
这样造成的问题就是server上的repo必须得是bare. 如果它含有working tree, 那么就算将server上的repo里的.git目录作为upstream加入, pull的时候没有问题, 但push的时候也会有问题
$ git remote add some_remote_name ssh://apc999@myhost/home/apc999/some-repo/.git
$ git pull some_remote_name master
.. works file ...
$ git push --set-upstream ssh://apc999@myhost/home/apc999/some-repo/.git master
...
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/home/apc999/some-repo'

如何使用bare repo

1 建立一个bare的git repo

$ git clone --bare my_project_on_github my-project.git
注意上面命令和普通git clone不一样的地方在于参数--bare. 普通的git clone命令会生成一个working tree和.git文件夹, 而--bare这样复制出来的bare repo就不包括working tree.

2 bare和 non-bare repo的转换

比如你的/home/apc999/my-project是一个git working tree
$ cd /home/apc999/my-project
$ git config --bool core.bare true

3 更新bare repo

注意, 在一个bare repo里, 不能使用git pull来更新这个repo. 而需要使用git fetch

Friday, July 12, 2013

[git] 一些git的使用小技巧

记录一下我在平时使用git时候的一些小技巧. 关于git更全面的使用介绍(比如整个工作的流程 ), 参见我之前写的的"git 笔记".

  • 查看my_file和之前版本的区别
    $ git diff HEAD my_file #latest version checked-in
    $ git diff HEAD~1 my_file #previous of the latest version
    $ git diff HEAD~2 my_file #previous ^ 2 of the latest 
  • 跳过git add而直接commit当前修改过的文件中那些被track的
    $ git commit . -m "foo"
  • 把track的文件中所有被修改的都加入index
    $ git add -u
  • 和HEAD比较, 当前工作目录下有那些具体的修改(可能跨几个文件)
    $ git diff HEAD
  • 查看每次commit都改动了哪些文件
    $ git log --stat
    查看上一次commit有哪些文件被改动
    $ git log -n 1 --stat
  • 将当前branch rebase到本地的master branch上. 也就是当前branch的local commit会出现在commit log的最后
    $ git rebase master
  • 指定从origin/master 来更新
    $ git rebase origin master
  • 设置gitignore_global 建立~/.gitignore_global, 然后执行
    $ git config --global core.excludesfile ~/.gitignore_global
  • 查看当前git的config
    $ git config -l
  • 设定当前git repository的email
    $ git config user.email "apc999@youremail.com"
    查看当前git repository的email
    $ git config user.email

Tuesday, July 10, 2012

建立/使用gitotlite来搭建自己的git 服务器


建立Gitolite服务器


我使用了gitolite来建立自己的git server http://wiki.dreamhost.com/Gitolite

在Gitolite服务器上添加一个新的repository


gitolite的admin操作不在服务器本机上进行. 相反, 你需要先把gitolite的admin相关repo给clone下来, 在你本地修改以后, push回去
$ git clone git@my_git_server_address:gitolite-admin
然后修改conf/gitolite.conf这个文件, 比如我的这个文件内容为
repo gitolite-admin
    RW+     =   apc999

repo my_fav_repo
    RW+     =   apc999
然后把改变给push回去
$ git add conf/gitolite.conf
$ git commit . -m "add a new project"
$ git push

把本地文件导入


$ cd /your/path/to/push
$ git init
$ git add .
$ git commit -m "init import"
$ git remote add origin git@my_server:my_fav_repo.git
$ git push origin master

Tuesday, May 17, 2011

Indefero

在Indefero上new了一个project之后, 想用git pull一个空的source tree下来.但是发现git总是报错
Cloning into hashfilter...
fatal: '/var/local/indefero/git/repositories/myproj.git' does not appear to be a git repository
fatal: The remote end hung up unexpectedly
折腾了很久, 后来发现原来Indefero上新建的project是没有repo的. 第一次使用的时候需要自己import.所以要在自己的目录里
git init
git add .
git commit -m "initial import"
git remote add origin git@a.b.c.d:yourproj.git
git push origin master
然后就可以在Indefero上看见自己的source tree了

Wednesday, July 21, 2010

git笔记

git是Linus亲自操刀设计实现的版本控制工具(据说Linus在两个礼拜内搞定了git,教主威武).git功能上比SVN要强,但是学习曲线也陡了很多. 这里是我对SVN的笔记


文档

关于git的教程和手册网上很多, 但很多写的都不好.最推荐:
Git Workflow (这里的diagram非常给力)
How to version projects with Git 图文并茂,有木有?
git quick reference

这几个也不错
Git Magic 中文,英文
Pro Git(中文)

其他的还有:
Understanding Git Conceptually
Git User Manual写的比较晦涩
Git - SVN Crash Course. 我觉得帮助不是很大, 因为git和svn在用法上区别是在太大了
GIT cheat sheet这个不错
Basic Branch Merging
关于stash, log, 以及gitconfig的用法

用法

初始化一个workspace
从远端克隆一个repository到本地
$git clone git://path/to/your/git/repo
注意git是一个分布式的版本控制系统. 如果你从server1上clone的repo, 那么以后server1就相当于你的源.如果以后server2再clone了你的repo, 你就相当于server2的源.我发现这种结构很方便调试 -- 在其他机器上可以从我本地repo来pull 文件. clone的时候除了git协议还可以使用ssh, 比如
$git clone ssh://hostname/path/to/your/git/repo

工作流程
进入你的工作目录,通常大家首先将本地repository更新到源上的最新版本(大致相当于svn update, 但是因为git是分布式的设计,更新到源上最新未必是全局最新). 下面的命令从origin 这个remote 更新 master这个branch的update.
$git pull origin master
未必需要从自己所在的branch来更新. 比如你实际在master这个branch, 你也可以更新branch foo的东西,即使你还是留在master这个branch里:
$git pull origin foo
有时候每次都是默认同样的remote同样的branch,那么我们可以修改工作目录下的.git/config文件,
[branch "master"]
    remote = origin
    merge = refs/heads/master
这样就可以直接用下面命令来更新而无须指定remote和branch
$git pull


做了修改后, 为了查看当前工作目录的状态,比如哪些文件被改动, 哪些文件没有commit, 可以使用status (大致相当于svn st)
$git status
如果需要git status的时候忽略某些文件,比如.o文件或者.pyc文件, 我们可以在.gitignore这个文件中加入两行*.o以及*.pyc.

修改了本地的文件后, 需要将其用add命令将其加入index. svn里没有index的概念,而且add这个命令在svn中是将一个文件加入版本控制. 而git里, 一个文件每次的修改都会需要你用add加入index后才能commit.
$git add file1 file2
然后将index的修改commit到本地.
$git commit file1 file2 -m "go! commit file1, file2"
以上两步(add, commit)也可以被合并成一步
$git commit -a file1 file2 -m "go! commit file1, file2"
如果需要更改上一次commit的信息
$git commit --amend -m "this is the right commit!"

这个时候文件只是在本地commit, 如果希望更新到远端的repositiory中, 还需要push(和更新源到本地的pull命令对应). 默认git push的话是push到origin的master分支
$git push
你也可以指定remote repo以及branch
$git push repo5 branch12
如果当前工作目录下checkout了多个branch, 但是你一般只会push正在tracking的这个branch,可以在config文件(比如.git/config)里设置
[push]
    default = tracking
或者使用命令
$git config push.default tracking

提取给定版本 checkout
checkout最新commit里的foo
$git checkout foo
如果需要提前两个版本的foo,可以
$git checkout HEAD~2 foo
从最新的stash(stash@{0})里checkout foo这个文件
$git checkout stash@{0} foo

撤销更改 revert, checkout, reset
比如你刚刚修改了一个文件foo, 但现在想撤销这个更改. 这需要根据foo的状态来选择不同的方法:
  • Changed but not updated: 如果一个文件foo被删除了, 或者被修改了但完全还在本地workspace中,还没有使用git add把foo加入index当中, 那么我们只需要重新checkout 这个文件
    $git checkout foo
    如果你不幸有一个branch也叫foo
    $git checkout -- foo
    如果你需要恢复当前目录所有文件
    $git checkout . 
    有时候需要把当前workspace中所有的修改都撤销,可以简单的使用:
    $git stash
  • Changes to be committed: 这时候修改已经加入index当中,但是还没有commit 提交到本地repo,我们可以使用reset使的当前workspace回到上一个commit的时候, 然后使用checkout恢复修改.
    $git reset HEAD foo
    $git checkout foo
    撤销所有index当中的修改:
    $git reset HEAD
  • 如果已经使用git commit把修改提交到了本地repo当中, 我们可以把本地repo rollback到上一次commit前的状态:
    $git reset --hard HEAD~1
    --hard选项会overwrite的所有的改动.

    如果我们只需要undo某一次的commit,可以使用git revert:
    $git revert b38155cbf671d55ceb027687c39508de8cef2463
    这样实际上是提交了另一个commit来抵消指定的commit.

查看日志 log
当前branch的commit日志
$git log
当前branch的commit日志,显示内容
$git log -p
当前branch的commit日志,但只显示上3次提交的内容
$git log -3
当前branch的commit日志,但只显示被修改的文件名
$git log --name-only

处理冲突 conflict
参考手册http://www.kernel.org/pub/software/scm/git/docs/user-manual.html
以及 http://www.kernel.org/pub/software/scm/git/docs/git-push.html

pull的时候如果有本地修改会导致无法成功. 如果希望能把远端的commit merge到本地
$git checkout -m foo
Auto-merging foo
有可能会失败,这时候就需要你手动来编辑这个文件来merge了

push的时候, 当前commit到远端repo的时候,有时候会出现如下问题:
$git push
To git@example.come:example.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@example.com:example.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.
这是由于你的工作目录没有update到最新的版本.换言之有人在你上一次pull之后又push了新的commit进去.所以如果你的push生效, 可能会导致别人push的commit失效. 解决方法有两种:
$git pull
$git push
这种方法先把当前工作目录更新到最新 -- 这一步可能会产生冲突. 解决冲突后把更新提交. 这种方法会产生两个commit信息. 一次是说你自己的更新,另一次是将你的分支和repo上分支的merge.
如果不希望产生两次commit信息, 可以用第二种方法:
$git pull --rebase
$git push
这种方法直接将你的commit重新rebase到最新的版本上, 然后再进行提交.

分支 branch
一个repo可以有不同的分支,branch命令查看本地已有的分支, *表示当前使用的branch
$git branch
* master
  testbranch
可以用branch -r 查看remote所有的分支
$git branch -r
也可以用branch -a 同时查看local和remote所有的分支
$git branch -a
以当前branch的HEAD为起始点,新建新的branch叫newbranch
$git branch newbranch
也可以指定起始点(比如testbranch), 创建一个新的分支叫newbranch
$git branch newbranch testbranch

将当前分支从master切换到testbranch
$git checkout testbranch
$git branch
  master
* testbranch

删除一个local的branch
$git branch -d branch_to_delete
删除一个remote的branch
$git branch push origin --delete branch_to_delete
远端 remote
查看当前branch有哪些remote
$git remote
origin
查看当前branch有哪些remote,显示具体一些的信息
$git remote -v
origin git@example.com:bar.git (fetch)
origin git@example.com:bar.git (push)

查看origin这个remote的具体信息
$git remote show origin
一个repo可以有多个remote.比如给分支branch_foo添加一个新的remote:
$git remote add branch_foo git@host-for-new-remote:repo-name.git
将新添加的remote添加为tracking的branch
$git branch --set-upstream branch_foo   remote_bar/branch_foo

查看历史版本
查看某个版本(some-sha1)的某个文件
$git show some-sha1-number:some-file
查看某个版本(some-sha1)的和最新版本(HEAD)之间的所有区别
$git diff some-sha1 HEAD
查看某个版本(some-sha1)的和最新版本(HEAD)之间的某个文件的区别
$git diff some-sha1 HEAD --somefile

比较当前branch和另外一个branch比如叫foo的区别
$git diff foo
比较branch foo和另外一个branch bar的区别
$git diff foo bar
查看远端repo里的文件
查看当前HEAD中所有加入git管理的个文件
$ git ls-tree -r  HEAD

其他相关工具
tig - text-mode interface for git 设置git ignore的方法: http://help.github.com/ignore-files/