[已迁移]使用git管理代码仓库
GIT
1 Git vs SVN
Git是分布式的,SVN是集中式的
- Git复杂概念多,SVN简单易上手
- Git分支廉价,SVN分支昂贵
在版本管理里,分支是很常使用的功能。在发布版本前,需要发布分支,进行大需求开发,需要 feature 分支,大团队还会有开发分支,稳定分支等。在大团队开发过程中,常常存在创建分支,切换分支的需求。
Git 分支是指针指向某次提交,而 SVN 分支是拷贝的目录。这个特性使 Git 的分支切换非常迅速,并且创建成本非常低。
而且 Git 有本地分支,SVN 无本地分支。在实际开发过程中,经常会遇到有些代码没写完,但是需紧急处理其他问题,若我们使用 Git,便可以创建本地分支存储没写完的代码,待问题处理完后,再回到本地分支继续完成代码。
2 git核心概念
Git 最核心的一个概念就是工作流。
-
工作区(Workspace)是电脑中实际的目录。
-
暂存区(Index)类似于缓存区域,临时保存你的改动。
-
仓库区(Repository),分为本地仓库和远程仓库。
一般来说,记住以下命令,便可进行日常工作了
3 初始化
安装服务器
centos 7 yum install git rhel6 wget -c https://mirrors.edge.kernel.org/pub/software/scm/git/git-1.8.4.tar.gz yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel ./configure --prefix=/usr make all doc make install install-doc install-html 编译的报错忽略
Git global setup git config --global user.name "高涛" git config --global user.email "gaotao@huored.com" Create a new repository 新建repo git clone git@gitlab.ops.net:gaotao/test.git cd test touch README.md git add README.md git commit -m "add README" git push -u origin master Existing folder 已存在的目录 cd existing_folder git init git remote add origin git@gitlab.ops.net:gaotao/test.git git add . git commit -m "Initial commit" git push -u origin master Existing Git repository 本地已存在的一个repo cd existing_repo git remote rename origin old-origin git remote add origin git@gitlab.ops.net:gaotao/test.git git push -u origin --all git push -u origin --tags
4 配置
# 列举所有配置 $ git config -l # 为命令配置别名 $ git config --global alias.co checkout $ git config --global alias.ci commit $ git config --global alias.st status $ git config --global alias.br branch # 设置提交代码时的用户信息 $ git config [--global] user.name "[name]" $ git config [--global] user.email "[email address]" $ git config remote.origin.url git@github.com:richardzgt/AssetMP.git 使用ssh方式可以免密码 git remote add origin git@github.com:richardzgt/PyLogin.git
Git 用户的配置文件位于 ~/.gitconfig
Git 单个仓库的配置文件位于 ~/$PROJECT_PATH/.git/config
5 增删文件
# 添加当前目录的所有文件到暂存区 $ git add . # 添加指定文件到暂存区 $ git add <file1> <file2> ... # 添加指定目录到暂存区,包括其子目录 $ git add <dir> $ git mv [file-original] [file-renamed] # 删除工作区文件,并且将这次删除放入暂存区 git rm <file> # 停止追踪指定文件,但该文件会保留在工作区 git rm -r --cached .venv git rm --cached Pipfile.lock # 删除仓库上的文件 git remote rm <file> 在.gitignore配置不需要同步的文件 *.lock .venv *.log
6 分支
# 列出所有本地分支 $ git branch # 列出所有本地分支和远程分支 $ git branch -a # 新建一个分支,但依然停留在当前分支 $ git branch [branch-name] # 新建一个分支,并切换到该分支 $ git checkout -b [new_branch] [remote-branch] # 切换到指定分支,并更新工作区 $ git checkout [branch-name] # 合并指定分支到当前分支 $ git merge [branch] # 选择一个文件合并到分支/master git checkout master # 切换到master git checkout --patch aliyun_api xdiscovery/plugins/aliyun.py # 把aliyun_api这个分支的某个文件合并到当前分支(master) # 选择一个 commit,合并进当前分支 $ git cherry-pick [commit] # 删除本地分支,-D 参数强制删除分支 $ git branch -d [branch-name] # 创建远程分支(本地分支push到远程) git push origin [name] # 删除远程分支 $ git branch -a * master remotes/origin/master remotes/origin/v1.0 $ git push origin --delete v1.0
7 tag(标签)
# 创建标签 先切换到对应的branch分支 git tag v1 git tag -a v1 -m "0.1.2版本" # 切换标签和分支相同 git checkout [tagname] # 推送到远程 git push origin v1 或者 git push origin --tags 把所有的本地标签一次性推上去 # github 中删除release/tag只能在命令行执行,不能在界面点击操作 git tag -d [tag] git push origin :[tag] 比如要操作一个v1的标签 git tag -d v1 git push origin :v1
8 提交
# 提交暂存区到仓库区 $ git commit -m [message] # 提交工作区与暂存区的变化直接到仓库区 $ git commit -a # 提交时显示所有 diff 信息 $ git commit -v # 提交暂存区修改到仓库区,合并到上次修改,并修改上次的提交信息 $ git commit --amend -m [message] # 上传本地指定分支到远程仓库 $ git push [remote] [remote-branch]
9 拉取
# 下载远程仓库的所有变动 $ git fetch [remote] # 显示所有远程仓库 $ git remote -v # 显示某个远程仓库的信息 $ git remote show [remote] # 增加一个新的远程仓库,并命名 $ git remote add [remote-name] [url] # 删除远程仓库 $ git remote rm [name] # 修改远程仓库 $ git remote set-url --push[name][newUrl] # 推送远程仓库 $ git push [remoteName] [localBranchName] # 取回远程仓库的变化,并与本地分支合并 $ git pull [remote] [branch] # 取回远程仓库的变化,并与本地分支变基合并 $ git pull --rebase [remote] [branch]
10 撤销
# 恢复暂存区的指定文件到工作区(比如文件在工作区被删除,就要从暂存区恢复,但是首先先要commit到暂存区),使用git status先看下状态 $ git checkout [file] # 恢复暂存区当前目录的所有文件到工作区 $ git checkout . # 恢复工作区到指定 commit,比如master、tag_name、commit_id $ git checkout [commit] # 重置暂存区的指定文件,与上一次 commit 保持一致,但工作区不变 $ git reset [file] # 重置暂存区与工作区,与上一次 commit 保持一致 $ git reset --hard # 重置当前分支的指针为指定 commit,同时重置暂存区,但工作区不变 $ git reset [commit] # 重置当前分支的HEAD为指定 commit,同时重置暂存区和工作区,与指定 commit 一致 $ git reset --hard [commit] git log // 查看版本后 git reset --hard 261896678417746b5b932a2865aa3f2728d63c2e //恢复到指定版本 git reset --hard HEAD^ // 回复到上个版本 # 新建一个 commit,用于撤销指定 commit $ git revert [commit] # 将未提交的变化放在储藏区 $ git stash # 将储藏区的内容恢复到当前工作区 $ git stash pop # 查看stash空间的内容 $ git stash show
git stash
1、git stash 备份当前工作区的内容,保存到git 栈中,从最近的一次commit中读取相关内容 2、git pull 或者做其他的工作 3、git stash pop 从git栈中获取到最近一次stash进去的内容,恢复工作区的内容。。获取之后,会删除栈中对应的stash。。 由于可能会stash多次,git使用栈管理,我们可以使用git stash list查看所有的stash git stash list 显示git栈中的所有工作区内容的备份, 比如使用git stash apply stash@{1},就可以把版本号为stash@{1}的备份取出,不会删除对应的stash。。0为最新版本 git stash clear 清空git栈
还可以git reset --hard放弃本地修改,然后就可以git pull了。。但是不推荐使用gitreset --hard指令,实在是太危险啦!
但是git stash pop取出备份的时候也会出现冲突
比如,有个文件login.java,,你修改了一段代码,git stash保存以后,你从服务器上继续git pull了别人的代码,,
如果此时,别人的代码也修改了login.java。。。
此时当我们使用git stash pop 的时候,就会发生冲突,因为我们的修改不是基于最新的pull下来的文件的基础上。。所以git很难判断,
解决方法:
备份我们修改后的文件,,删除程序文件中我们所做的修改,重新pull,,然后在用我们备份好的文件替换掉,,再push上去即可。。
11 查询
# 查看工作区文件修改状态 $ git status # 查看版本库修改记录,列出所有对此文件修改的历史记录 $ git log git log ucloud_uhas_api.py # 查看某个文件的历史具体修改内容,列出所有修改内容,十分实用 $ git log -p [file] # 查看工作区文件修改具体内容 $ git diff [file] # 查看暂存区文件修改内容 $ git diff --cached [file] # 查看某人提交记录 $ git log --author=someone # 查看某次提交具体修改内容 $ git show [commit] # 查看某个文件的更新 git show 4e3f163788608c0a354f9c690fd456c618a17b3d ucloud_uhas_api.py git show 73c023dadc5a1fa5540d47eefae083bdebac7745 ucloud_uhas_api.py
12 代码合并
当仓库代码和本地已提交的代码冲突时,会提示:
更新 77582d4..702d425
error: 您对下列文件的本地修改将被合并操作覆盖:
1 我们可以使用强行更新掉:
git fetch git reset --hard origin/master HEAD 现在位于 702d425 [FIX] update the pipfile, correct `pymysql` version as `0.9.3` git pull 已经是最新的。
2 手工处理有冲突的部分
$ git pull remote: Counting objects: 6, done. remote: Compressing objects: 100% (6/6), done. remote: Total 6 (delta 4), reused 2 (delta 0) 展开对象中: 100% (6/6), 完成. 来自 gitlab.ops.net:mlyw/vm_manager 30460e4..fe01e2b master -> origin/master 更新 30460e4..fe01e2b error: 您对下列文件的本地修改将被合并操作覆盖: script/dev/docker/start.sh 请在合并前提交您的修改或者保存进度。 终止中 $ git status 位于分支 master 您的分支落后 'origin/master' 共 1 个提交,并且可以快进。 (使用 "git pull" 来更新您的本地分支) 尚未暂存以备提交的变更: (使用 "git add <文件>..." 更新要提交的内容) (使用 "git checkout -- <文件>..." 丢弃工作区的改动) 修改: start.sh 未跟踪的文件: (使用 "git add <文件>..." 以包含要提交的内容) ../../../conf/1 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
git pull远程仓库出错,本地冲突没有add也没有commit到本地仓库
git merge start.sh merge:start.sh - 不能合并
用merge提示不能合并
$ git add start.sh $ git commit -m '[merge] start.sh'
把冲突文件先提交到本地仓库
$ git merge start.sh error: 无法合并,因为您有未合并的文件。 提示:请在工作区改正文件,然后酌情使用 'git add/rm <文件>' 命令标记 提示:解决方案并提交。 fatal: 因为存在未解决的冲突而退出。
没发自动merge,只能手工咯
vi start.sh #!/bin/bash <<<<<<< HEAD # @Author: gaotao99 ======= # @Author: gaotao100 >>>>>>> fe01e2be0d4bc0271c934db1a618bc89a64aab7e # @Date: 2018-05-10 10:53:09 # @Last Modified by: gaotao # @Last Modified time: 2018-05-23 18:35:06 RUN_SCRIPT='run.sh' WORK_DIR="/bench/app/deploy/" UTIL_URL="http://yum.ops.net/script/docker/run.sh" source include/linux/setenv.sh retry=10 count=$retry
远程仓库和本地仓库的差异就会显示出来,删除或者保留直接编辑操作
$ git add start.sh $ git commit -m '[merge] start.sh' $ git push 对象计数中: 7, 完成. Delta compression using up to 8 threads. 压缩对象中: 100% (7/7), 完成. 写入对象中: 100% (7/7), 583 bytes | 0 bytes/s, 完成. Total 7 (delta 5), reused 0 (delta 0) To gitlab.ops.net:mlyw/vm_manager.git fe01e2b..9ab7dae master -> master
最后再提交一次完事
13 git commit message 的格式
每次提交,Commit message 都包括三个部分:Header(必须),Body 和 Footer可选。
Header部分只有一行,包括三个字段:type
(必需)、scope
(可选)和subject
(必需)。
1)type
[FEAT]:新功能(feature) [FIX]:修补bug [DOCS]:文档(documentation) [STYLE]: 格式(不影响代码运行的变动) [REFACTOR]:重构(即不是新增功能,也不是修改bug的代码变动) [TEST]:增加测试 [CHORE]:构建过程或辅助工具的变动
如果type
为feat
和fix
,则该 commit 将肯定出现在 Change log 之中。其他情况(docs
、chore
、style
、refactor
、test
)由你决定,要不要放入 Change log,建议是不要
2)scope
scope
用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
(3)subject
subject
是 commit 目的的简短描述,不超过50个字符。
- 以动词开头,使用第一人称现在时,比如
change
,而不是changed
或changes
- 第一个字母小写
- 结尾不加句号(
.
)
14 遇到过的一些问题
git clone 时出现 fatal: Unable to find remote helper for ‘http’ 的解决方式
1 找到 git-core 2 加到环境变量里面: /usr/libexec/git-core:$PATH (gnome-ssh-askpass:29241): Gtk-WARNING **: cannot open display: 1 unset SSH_ASKPASS git push //或者git push -u origin master
error: The requested URL returned error: 401 Unauthorized while accessing http://git.wangshibo.net:8081/weixin/weixin.git/info/refs
error: Your local changes to the following files would be overwritten by merge:
protected/config/main.php
Please, commit your changes or stash them before you can merge.
Git代码冲突常见解决方法: 在代码的.git/config文件内[remote "origin"]的url的gitlab域名前添加gitlab注册时的“用户名:密码@” url = http://gaotao:30402104@gitlab.ops.net/gaotao/dev-nginx-config.git error: Your local changes to the following files would be overwritten by merge: 解决办法: 如果希望用代码库中的文件完全覆盖本地工作版本: git reset --hard git pull 如果希望保留生产服务器上所做的改动,仅仅并入新配置项, git stash git pull git stash pop
Merging is not possible because you have unmerged files
这个出题出现在代码合并的过程上,原因是不仅是remote的文件代码变了,本地的文件代码也变了,所以造成了冲突。
If you have fixed the conflicts you need to add the files to the stage with git add [filename], then commit as normal.
用git diff或者git status 查看哪些文件冲突,有冲突的会提示:
就是你手动修改git提示有错误的文件,修改之后,添加有冲突的文件: git add [被修改的冲突文件], 最后,按照普通提交那样,提交有冲突的问题: git commit [修改后的冲突文件] -m “注释内容”
15 gitlab server
配置git对接ldap服务器
label: 'LDAP' host: '10.0.0.200' port: 389 uid: 'uid' method: 'plain' # "start_tls" or "simple_tls" or "plain" bind_dn: 'CN=Manager,DC=xxxx,DC=com' password: 'XXXXX' base: 'DC=xxxx,DC=com'
gitlab-ctl reconfigure gitlab-ctl restart/tail
配置邮箱
# gitlab-rails console Loading production environment (Rails 4.2.8) irb(main):001:0> irb(main):002:0* irb(main):003:0* irb(main):004:0* irb(main):005:0* Notify.test_email("xufei_@hotmail.com","nihao","gitlab").deliver_now Notify#test_email: processed outbound mail in 183.0ms Sent mail to xufei_@hotmail.com (53.2ms) 。。。。。。。。。。。。
16 备份恢复
首先两个服务器先做互信
# 备份脚本 git_bak.sh #!/bin/bash # BACK_DIR="/var/opt/gitlab/backups" rm -rf ${BACK_DIR}/* gitlab-rake gitlab:backup:create cd $BACK_DIR FILE=$(ls -r *.tar|head -1) scp $FILE 10.4.230.9:${BACK_DIR} # 恢复脚本 git_restore.sh #!/bin/bash BACK_DIR="/var/opt/gitlab/backups" cd $BACK_DIR latest_file=$(ls -r *.tar|head -1) chmod 777 $latest_file sub_file=$(echo $latest_file|awk -F"_gitlab" '{print $1}') echo $sub_file gitlab-ctl stop unicorn gitlab-ctl stop sidekiq gitlab-rake gitlab:backup:restore BACKUP=${sub_file} force=yes # 强制执行 if [ $? -eq 0 ]; then echo "restore success" gitlab-ctl reconfigure gitlab-ctl restart # 要有3分钟的等待时间 cd $BACK_DIR ls *.tar|grep -v $latest_file|xargs rm -rf # 只保留一个文件 rm -rf /var/opt/gitlab/git-data/repositories.old.* # 删除历史临时repository文件 else echo "restore failed!" fi
svn
1 下载
svn checkout svn://192.168.21.103/ 非交互式 svn --username=xxxx --password=xxxxxx -q checkout http://huored.gicp.net:8088/svn/xxxxx/trunk ./ --non-interactive --trust-server-cert
2 导入
导入项目文件(导入在根目录上) sudo svn import PythonLearn svn://192.168.21.103/ -m "import"
3 更新
SVN update 将版本库的修改合并到工作副本中 svn commit svn list svn://183.129.172.71/PythonLearn/Project/MyStock svn checkout svn://183.129.172.71/Project/jumpserver-master 更新到指定版本 svn checkout http://ip/trunk ./ -r 308
4 删除
svn delete svn://183.129.172.71/PythonLearn/Project/Salt/srv -m 'delete salt'
5 查看
注意 加了username可以指定用户(可能默认下用户不能登录) svn --username=gaotao list http://huored.gicp.net:8088/svn/opscript 1)svn status path(目录下的文件和子目录的状态,正常状态不显示) 【?:不在svn的控制中;M:内容被修改;C:发生冲突;A:预定加入到版本库;K:被锁定】 2)svn status -v path(显示文件和子目录状态) 第一列保持相同,第二列显示工作版本号,第三和第四列显示最后一次修改的版本号和修改人。 注:svn status、svn diff和 svn revert这三条命令在没有网络的情况下也可以执行的,原因是svn在本地的.svn中保留了本地版本的原始拷贝。 简写:svn st 或者直接svn delete test.php 然后再svn ci -m ‘delete test file‘,推荐使用这种 简写:svn (del, remove, rm) 查看日志 svn log path 例如:svn log test.php 显示这个文件的所有修改记录,及其版本号的变化 9、查看文件详细信息 svn info path 例如:svn info test.php
6 问题处理
/usr/bin/env LC_ALL=C svn log --xml -l 30 --username='zgt' --password='xxx' --non-interactive --trust-server-cert
subversion/libsvn_repos/log.c:1073: (apr_err=220001)
svn: Item is not readable
处理: 修改服务器 authz 文件,把 读权限加到每个库上 [repository:/] scmroad = rw || || VV [repository:/] *=r scmroad = rw
svn: 方法 OPTIONS 失败于 200 OK
1 将 10.0.0.199 huored.gicp.net 配置到 /etc/hosts
2 rm -rf ~/.subversion/auth/svn.simple/*
7 svn server
./configure --prefix=/usr/local/svn --with-apxs=/usr/local/httpd-2.2.19/bin/apxs --with-apr=/usr/local/apr/bin/apr-1-config --with-apr-util=/usr/local/apr-util/bin/apu-1-config --with-ssl --with-sqlite=/usr/local/sqlite --with-neon=/usr/local/neon --with-zlib=/usr --enable-maintainer-mode 启动 /usr/local/subversion/bin/svnserve -r /usr/local/subversion/svndata/repos -d