1

1. git使用简介

1.1 git的配置文件

三级配置文件

  • 项目级

    • .git/config
    • git config user.name 'username'
  • 用户级

    • ~/.gitconfig
    • git config —global user.name 'username'
  • 系统级

    • /etc/gitconfig
    • git config —system user.name 'username'

初始化的git配置

  • 用户名 & 邮箱
git config user.name 'username'
git config user.email 'email@xxx.xxx'
  • 是否忽略文件权限变化
git config core.filemode false
  • 编辑器的配置
// 设置编辑器
git config core.editor vim 

// 合并工具
git config merge.tool

// 设置diff工具 
git config --global diff.tool vimdiff
git config --global difftool.prompt No
  • 着色
git config --global  color.ui true

// git branch/diff/status 着色
git config --global color.status auto
git config --global color.diff auto
git config --global color.branch auto
  • 将git commit 编辑器改为vim

    1. 编辑.git/config文件。在core中添加editor = vim。如此以后在使用git的时候就自动使用vim作为编辑器
    2. 除去git,系统中有其他的也会调用编辑器,可以使用一下命令来全局配置编辑器的选择:
update-alternatives --config editor

git config --global core.editor vim

sudo update-alternatives --config editor
  • 本地分支绑定远程分支

如果你最近更新了 Git,你可能会在执行 git push 时看到如下消息:

warning: push.default is unset; its implicit value is changing in 
Git 2.0 from 'matching' to 'simple'. To squelch this message 
and maintain the current behavior after the default changes, use: 

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use: 

  git config --global push.default simple
  • Matching

matching 参数是 Git 1.x 的默认行为,其意是如果你执行 git push 但没有指定分支,它将 push 所有你本地的分支到远程仓库中对应匹配的分支。

  • Simple

而 Git 2.x 默认的是 simple,意味着执行 git push 没有指定分支时,只有当前分支会被 push 到你使用 git pull 获取的代码。

修改默认设置
从上述消息提示中的解释,我们可以修改全局配置,使之不会每次 push 的时候都进行提示。对于 matching 输入如下命令即可:

git config --global push.default matching

而对于 simple ,请输入:

git config --global push.default simple

1.2 配置私钥

需要配置自己的私钥. 在 ~/.ssh/id_rsa 文件, 此文件的权限必须是 0600

ssh可以通过-i使用指定的私钥文件,如:

ssh -i ~/mykey_rsa username@host

但是走ssh协议git却没有类似的参数可以指定,只能是使用用户默认的ssh私钥
如果要在git中使用特定的ssh key文件,可以在.ssh/config中增加一个host配置,格式如:

host 名称(自己决定,方便输入记忆的)
    hostname 主机名
    port   端口
    user 登录的用户名
    Identityfile 私钥文件路径

例如添加一个github的配置:

host mygithub
    user git
    hostname github.com
    port 22
    identityfile ~/.ssh/id_rsa_git

之后就可以使用mygithub这个名字来代替git urlgit@github.com部分了。

ssh -T mygithub /*测试配置*/
git clone ssh://mygithub:{gitusername}/{projectname}.git /*{gitusername}部分是github的username。 */

1.3 管理代码

如果在服务器已经部署你的公钥了, 那么你可以尝试从服务器上checkout代码了

git clone git@***.git
git clone git@***.git dirname // [将代码检出到dirname目录下]

cd dirname
git branch // 查看当前所在分支名

git checkout –b new_branch [target_branch] // 创建new_branch并切换至new_branch分支, target_branch默认当前分支 

git checkout target_branch // 切换至target_branch, target_branch不存在会报错(首先保证当前分支上没有改动, 才能进行切换)

git fetch origin // 获取远程所有有改动的分支
git merge origin/target_branch // 合并origin/target_branch分支代码至当前分支
git pull origin target_branch // 从远程 target_branch 分支拉代码

# 我想知道本次就那些文件进行过修改(我想确定分支上有没有修改)
git status

# 我想对比一下, 看看我到底进行了那些操作?
## 没有用git add
git diff
git difff origin/master  #跟远程的master对比
git diff origin/remote_branch #跟远程***_branch对比

## 已经使用过git add
git diff --cached
  
# 我修改之后,想放弃此次修改
git checkout xx_filename 
git stash

# 将自己的改动推到远程
git push origin current_branch

1.4 储藏

我的分支上有改动,但是我还想切换分支, 但我又不想提交我的改动, 使用 git stash 将改动储藏起来

  • 想要看我的储藏列表 git stash list
  • 想要应用储藏的内容 git stash apply –index(索引位置)
  • 想要查看我具体储藏了那些内容 git stash list -p
  • 删除储藏列表 gitstash drop 储藏名
  • 想要查看此次储藏的涉及到那些文件? git stash show stash:{0} 查看名为stash{0}缓存的文件列表
注意: stash 尽量少用,最好只缓存一次,不然顺序会搞不好弄乱的。

1.5 冲突

如果在 git mergegit pull 时, 发生冲突, 如果这些冲突是自己的原因产生的, 那么自己解决, 但是如果不是自己的原因, 那可以查看文件的日志(命令如下), 找到冲突的文件曾被谁更改过, 之后一起解决冲突

git log [–p ] // 文件名【-p可省略, 加上-p显示的是详细信息】

还有一种就是, 如果冲突的文件中没有新内容, 并且你可以找到一个最新的版本

例如: index.phpmaster上面是最新的, 而在本地冲突了, 但是你不想解决这个冲突, 可以执行以下操作(慎用, 执行完之后你自己的代码没有保留也没有备份)

   git add index.php
   git reset HEAD index.php //【慎用】
   git checkout origin/master index.php  

这样以后index.php就跟远程的master分支上一致了。

1.6 代码回退

git reset 回退以前某个版本
git reset 是指将当前head的内容重置,不会留log信息。

git reset HEAD filename  // 从暂存区中移除文件
git reset --hard origin/master // 将代码重置到origin/master
git reset --hard HEAD~3  // 会将最新的3次提交全部重置,就像没有提交过一样。
git reset --hard commit (38679ed) // 回退到 38678ed 版本 

根据--soft --mixed --hard,会对working tree和index和HEAD进行重置

命令模式 命令介绍 对比差异 备注
git reset --mixed xxx 此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commitindex信息 git diff 开发过程中的代码 --mixed后上一次的代码还在
git reset--soft xxx 回退到某个版本,只回退了commit的信息,不会恢复到index file一级。如果还要提交,直接commit即可 git diff --cached 本地缓冲区 --soft 后上一次的代码还在
git reset --hard xxx 彻底回退到某个版本,本地的源码也会变为上一个版本的内容 - 本地代码库 --hard后上一次的代码不在, 不能恢复

1.7 差异(diff)

比较两个分支的差异

git diff master...version_no/branch (3个点可以比较任意不同)
git diff A..B   B里面有而A没有的改变(2个点)标签biaoqian

git diff HEAD //与上次 commit 之间的差别(爸爸)
git diff HEAD^ //与上上次(爷爷)
git diff HEAD^^ //与上上上次(曾祖父)
git diff HEAD~5 //与前面第5次commit(好吧…祖先吧)
git diff HEAD^..HEAD //中间是两个点比较(爸爸)和(爷爷)的差别
git diff hash1..hash2 //比较 两个不同 hash 值记录之间的不同
git diff master dev //比较 branch 之间的不同
git diff --since=1.week.ago --until=1.minute.ago //还可以根据时间来比较哦

1.8 日志(log)

# 获取当前版本号
git log --pretty -1 | awk '/^commit/{print $2}'

# 默认 git log 样式不太好看,换成一个比较好看的
git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'

git log --pretty --oneline在一行显示 git log.
git log --oneline --stat ,显示每个文件的变化行数, --start参数是用来统计哪些文件被改动,有多少行被改动。
git log --oneline --graph,则可以图形化地显示 branch 的变化(方便查看 merge 变化)。
git log --until=1.minute.ago // 一分钟之前的所有 log
git log --since=1.day.ago //一天之内的log
git log --since=1.hour.ago //一个小时之内的 log
git log --since=1.month.ago --until=2.weeks.ago //一个月之前到半个月之前的log
git log --since ==2013-08.01 --until=2013-09-07 //某个时间段的 log
git log –stat –summary 查看每个版本变动的档案和行数

/**
 * %ad  author date  // 日期
 * %an author name // 作者名
 * %cn committer name //提交者姓名
 * %h SHA hash // hash值
 * %s subject //commit的描述
 * %d  ref names //对应的 branch 分支名
 */
git log --pretty=format:"%h %ad- %s [%an]"

1.9 忽略文件

# 执行命令
git config core.excludesfile

# 修改 .gitignore

# 修改 .git/info/exclude

git update-index --assume-unchanged database.php // 忽略在库中的文件的改变

# 添加一个忽略文件到 gitignore
git rm --cached your_ignored_file

1.10 标签管理(tag)

# 查看tag
$ git tag | grep dev

# 创建tag
$ git tag dev -m 'add dev tag'

# 把tag push到远程
$ git push origin dev
$ git push origin --tags

# 删除tag 本地删除
$ git tag -d dev

# 删除远程的tag:推一个空的上去 (有的gitlab没有删除远程tag的权限)
$ git push origin :refs/tags/dev

2. git代码合并方法总结

前面我们在管理代码时介绍了通过pullmerge合并代码, 在这里介绍git cherry-pickgit rebase

2.1 git merge

  • 用来合并两个分支的。
# 将b分支合并到当前分支
git merge branch

2.2 git cherry-pick

可以选择某一个分支中的一个或几个commit(s)来进行操作。

例如,假设我们有个稳定版本的分支,叫v2.0,另外还有个开发版本的分支v3.0,我们不能直接把两个分支合并,这样会导致稳定版本混乱,但是又想增加一个v3.0中的功能到v2.0中,这里就可以使用cherry-pick了。

## 先在v3.0中查看要合并的commit的commit id

git log
# 假设是 commit f79b0b1ffe445cab6e531260743fa4e08fb4048b

# 切到v2.0中
git check v2.0

# 合并commit
git cherry-pick f79b0b1ffe445cab6e531260743fa4e08fb4048b

2.3 git rebase

  • 类似git merge

但是两者又有不同,打个比方,你有两个抽屉A和B,里面都装了衣服,现在想把B中的衣服放到A中,
git merge是那种横冲直撞型的,拿起B就倒入A里面,如果满了(冲突)再一并整理;
而git rebase就很持家了,它会一件一件的从B往A中加,会根据一开始放入的时间顺序的来加,如果满了你可以处理这一件,你可以继续加,或者跳过这一件,又或者不加了,把A还原。

所以merge适合那种比较琐碎的,简单的合并,系统级的合并还是用rebase吧。

@TODO: 专业的区别请移步到这里合并和衍合

# 合并b
git rebase branch

# 处理完冲突继续合并
git rebase --continue

# 跳过
git rebase --skip

# 取消合并
git rebase --abort

变基
整合不同分支的修改主要方式: mergerebase

merge 将两个分支的最新快照, 以及共同的祖先进行三方合并, 生成一个新的快照并提交
rebase 提取在dev上引入的补丁和修改, 然后在master的基础上再应用一次. 就像”重新播放”一样, 将dev上的所有改动都移至master分支.
rebase 原理: 首先找到master/dev共同的祖先, 然后对比两个分支的历次提交, 提取相应的修改并存为临时文件. 并按照原有次序依次应用到另一分支.
rebase 风险: 不要对在你的仓库外有副本的分支执行变基. 变基实质是丢弃一些现有的提交, 然后相应的新建一些内容一样单实际上不同的提交.

2.4 变基(rebae) vs 合并(merge)

对尚未推送//分享给别人的本地修改执行变基操作清理历史, 从不对已推动至别处的代码执行变基操作.

git merge

  • 横冲直撞式的合并

git rebase

  • 类似于git merge
  • 温文尔雅式的合并
  • 依据提交的时间顺序来合并

2.5 多出来的commit

Git从将远程分支与本地分支合并可以有两种方式,一种是 git pull, 另一种是 git fetch + git rebase.
区分在于 git pull 是把远程分支代码拉下来,与本地分支进行merge操作,相当于git fetch + git merge

所以使用git pull命令之后,会自动多出来一次 mergecommit,从git log上面看到的情况就是多了一次commit, 而且git树也不那么整洁了。
推荐的做法是使用rebase而不是pull, 可以git fetch ; git rebase ,也可以git pull —rebase.

参考


蒋建勇
516 声望36 粉丝

行易难