Git读书笔记

git入门
简单写一下读后感。
我看完的参考书籍,建议先看译本,再看英文原版加深理解:

书名 Pro Git Version Control with Git
作者 Scott Chacon & Ben Straub Jon Loeliger & Matthew McCullogh
下载地址 在线查看 PDF

本地仓库操作

先熟练掌握本地仓库的操作才比较容易理解远程仓库

初次运行git前配置

git config --global user.name "YOUR_NAME"
git config --global user.email "YOUR_EMAIL"

若需要提交GPG签名

git config --global user.signingkey <public GPG key>
# <public GPG key>是你的公钥id,可以从gpg --list-keys查看

若要检查你的git配置,打开~/.gitconfig文件即可。这是我的配置文件:

[user]
    name = lixingcong
    email = lixingcong@xxx.xxx
    signingkey = xxx  # GPG公钥id(可选)
[push]
    default = simple  #使用新版的推送模式
[alias]
    ls = log -1 HEAD  # 别名,快捷命令
    ad = add *
    cm = commit -am
    lg = log --pretty=format:"%Cred%h%Creset-%cD-%Cgreen%s" --graph
    lg1 = log --pretty=oneline
    st = status -s
    ps = stash
[credential]
    helper = cache  #凭证缓存
[help]
    autocorrect = 15  #自动更正错误

也可以运行查看当前的configuration

git config list

开始工作!
初始化一个文件夹为git仓库:

mk /tmp/test && cd /tmp/test
git init

工作区与暂存区

你在资源管理器里面看到的文件是“工作区”,你通过git add跟踪的文件存放的缓存位置是“暂存区”。
当你的“工作区”与“暂存区”中的文件是一致的,可以称为“干净”的。反之,称为“脏的”。
查看当前的工作区与暂存区命令

git status

这个命令要尽量多使用,可以了解当前目录的情况。
执行status后,绿色的表示已经跟踪并且存放到暂存区(可以随时commit)。红色的表示尚未跟踪,使用

git add FILE-NAME

可以将该文件加入暂存区,生成一个缓存。(准确来说是一个object)
每次修改玩完一个文件,都需要加入暂存区。
看看当前目录下与暂存区有什么区别?

git diff

关于git diff,主要有三种使用方法:

git diff #工作区中文件更新了哪些内容
git diff HEAD #工作区与HEAD中内容对比
git diff --cached #暂存区将要添加到commit的内容

HEAD指针可以理解为最近一次的提交。

提交

通过add跟踪后并使用status确认工作区是干净的,可以进行提交。‘
这里的提交,不是指提交到github上面,而是提交到本地的git仓库。
因为每一个文件夹下都有一个隐藏的.git文件夹,存放提交的历史记录。

git commit -m "YOUR-COMMENT"

若文件file1已经被跟踪,后来又修改file1,可以使用-add选项,不需另外敲add跟踪

git commit -am "YOUR-COMMENT"

若提交后,发现文件A中某句话写错了,修改文件A后,可以追加提交:覆盖上一次提交。建议书写your-comment跟上一次提交一致。

git commit -am "YOUR-COMMENT" --amend

打标签

提交确认无误可以进行打标签,方便后期管理

git tag -a v1.0 -m "YOUR-COMMENT-ON-THIS-TAG"

如果已经有GPG key,可以对tag进行GPG签名。参数-s

git tag -a -s v2.0 -m "SIGN-MY-TAG"

验证tag签名

git -v v2.0

默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支:

git push origin [tagname]

注意:打标签有两种方式,一种称之为轻量级“lightweight”,另一种是正常的“annotated”。不带任何参数运行git tag就是轻量级,加上-a参数就是annotated

标签 命令 特点 用途
lightweight git tag v1.0 不含任何tagger信息,仅仅是将某次commit作为tag 不建议使用,但可以暂时作为二分查找bisect的临时标签
annotated git tag -a v1.0 完整的标签,含tagger所有信息:文字信息,作者,日期,GPG签名值 建议使用,作为正常发布的release tag

摘录一段关于lightweight标签的博客文章

So, how does one create a tag? Just git tag v1.0.0 right? WRONG. You usually don’t want to do that. In fact, some suggest that this command does the wrong thing by default. Without arguments, git tag creates a “lightweight” tag that is basically a branch that never moves.

Lightweight tags are still useful though, perhaps for marking a known good (or bad) version, or a bunch of commits you may need to use in the future. Nevertheless, you probably don’t want to push these kinds of tags.

上述意思就是不鼓励我们使用lightweight标签,不鼓励push到上游。

创建忽略提交名单

功能就是防止提交上去。忽略.o这种c语言中间文件例:

echo *.o > ./.gitignore

这个ignore文件支持通配符,参考pro-git.pdf

查看提交历史

基本命令:

git log

使用图的方式,比较直观看到当前更新了的分支。

git log --pretty=format:"%s--%h--%ci" --graph

查看上一次的提交

git log -1 HEAD

查看某个文件的提交历史

git log FILE-NAME

当然,可以按时间进行筛选结果,参考pro-git.pdf

使用二分搜索查找bug出处,详细看pro-git.pdf:

git bisect start
git bisect bad
git reset v1.4
git bisect good

查看某个文件在某次Commit的完整内容(比如前2个提交)

git show master~2:src/main.cpp

相对提交名

有时候使用相对提交名,以确定【相对本次提交】的【前某次提交】。

尖角符号^n表示本次提交的来自第n个祖先(纵向,相当于爸爸/叔叔/伯伯)
波浪号~n表示当前提交的前第n代(横向,相当于爸爸/爷爷/曾祖父)

恢复到某次提交

使用reset 命令,能把暂存区恢复到任意一次commit。
使用soft模式,将head指针指向新的提交,暂存区和工作区的内容不变。对当前工作具有最小的影响。

git reset --soft YOUR-COMMIT-SHA1

使用mixed模式,不改变工作区,只恢复暂存区。
因为默认reset就是mixed模式,所以这个命令省略mixed

git reset YOUR-COMMIT-SHA1

若使用--hard具有更大的威力,其破坏性更大,恢复也更彻底。

git reset --hard YOUR-COMMIT-SHA1

对比一下威力:

选项 soft mixed hard
恢复HEAD指针 yes yes yes
恢复暂存区 - yes yes
恢复工作区 - - yes

目录下文件移动与重命名

不建议使用system-commands,使用git专用的命令:

git rm FILE-NAME
git mv FILE1 FILE2

分支操作

使用分支是git的一大特色,加速开发。

查看分支:

git branch

新建分支:

git branch YOUR-BRANCH-NAME

切换分支:

git checkout YOUR-BRANCH-NAME

合并分支:

git merge ANOTHER-BRANCH-NAME

合并时候,通常会遇到冲突,平和冲突完毕,commit一次即可。

删除本地分支:

git branch -D [BRANCH_NAME]

保存进度:
有时候需要切换紧急的分支,而又不想提交当前的分支。可以将其暂存下来。
如果不暂存,会提示切换分支失败。
保存的地方类似一个栈的数据结构。“入栈”->“出栈”

git stash save

保存完毕,工作区是干净的,可以进行切换分支

恢复进度:
从紧急分支切换回来,需要恢复进度:

git stash pop

变基:参考pro-git.pdf,说白了就是改祖宗。

Youtube上一段几分钟的视频,讲得非常清楚。请自备梯子观看!

干货来了:

花了一小时,我详细地学习了台湾同胞写的一篇非常精彩的博客《Git-rebase 小筆記》,终于知道怎么用rebase了,功能非常强大!!可以实现分支之间的变基、单分支的历史commit的改写(改commit message,commit代码内容,变换commit顺序等),诸多功能。。说不清楚啊!!

基本命令:分支test_rebse的base-commit变为master

git checkout test_rebse
git rebase master

交互式变基:可以单分支的改写历史commit

git rebase -i COMMIT_SHA_ID_TO_CHANGE_START_FROM

删除远程分支:

git push origin :[BRANCH_NAME]

远程仓库操作

主要是在本机实现一下远程的模拟。

制作一个远程仓库

mkdir /tmp/remote_sample && cd /tmp/remote_sample
git init
git remote add origin /tmp/remote_sample
git config receive.denyCurrentBranch warn #允许从别的地方推送
echo hello world > test
git add test && git commit -m "INIT_COMMENT"

就可以简单制作一个服务器了,有趣的是只运行在自己电脑上!

添加一个合作者,如Tony,新建一个Tony分支,以防止推送到master主线:

git checkout -b Tony

当然,可以添加更多的参与者。这个remote仓库只需适时进行多分支合并到master。
多人开发某个分支会导致某些合并问题并造成覆盖。不建议多人公用一个master分支。
master分支作为最终产品发布的主线。

克隆,修改,提交一条龙

模拟一下github的克隆:

mkdir /tmp/local_test && cd /tmp/local_test
git clone /tmp/remote_sample
cd remote_sample

假如我是tony,我的原则:只在自己的分支进行工作。
为了不影响其他人分支,先切换自己的分支

git checkout Tony

想要进行提交,必须先跟上游服务器的HEAD一致。先从远处拉!

git pull

提示Already up-to-date 就把自己的Tony专属分支拿回来了。
若多人开发这个分支,在pull时候,会遇到冲突,也许你克隆仓库后没有及时跟上分支的步伐。
强烈建议一人一个分支。
修改过程模拟:

echo hello world from Tony! > test

按照正常步骤add , commit提交。最后推送给远处:

git push

remote端收到push后合并

当remote仓库管理者得知你推送后,考虑是否把tony跟master分支合并。
模拟一下合并:

cd /tmp/remote_sample
git checkout master
git merge Tony

修正冲突,合并。master分支变为新master,称为Fast-Forward,表示前进了一步

好奇,是否remote的Tony分支跟Tony的文件一致呢?
切换一下Tony分支看看:

git checkout Tony
git status

什么!有未提交的文件!这是什么鬼!
原因很简单,暂存区是tony提交的,在【Tony分支暂存区】test文件内容是Hello world form tony。
而在【remote工作区】的test文件内容是hello world。
因此造成一种“别人都改了,怎么我变化没有呢?”的错觉。实际上缓存区已经是Tony最新的副本了。
HEAD指针还停留在原来地方,落后于Tony分支。
证明一下:查看从工作区准备commit的内容

git diff --cached

可以看到,当前工作区居然想把Tony写的那句from tony删掉了。
因此,要想保持工作区与分支是一致的,使用reset更新当前工作区。
(不可逆的工作区清倒)

git reset --hard SHA1-OF-TONY-LAST-PUSH

Github常用操作

Fork

在以前,“fork”是一个贬义词,指的是某个人使开源项目向不同的方向发展,或者创建一个竞争项目,使得原项目的贡献者分裂。
在 GitHub,“fork”指的是你自己的空间中创建的项目副本,这个副本允许你以一种更开放的方式对其进行修改。

Pull Request

GitHub 设计了一个以合并请求为中心的特殊合作流程。不管你是在一个紧密的团队中使用单独的版本库,或者使用许多的“Fork”来为一个由陌生人组成的国际企业或网络做出贡献,这种合作流程都能应付。
流程通常如下:

  • Fork
  • 从 master 分支中创建一个新分支
  • 提交一些修改来改进项目
  • 将这个分支推送push到 GitHub 上
  • 创建一个合并请求Pull Request
  • 讨论(通过网页或者邮件),根据实际情况继续修改
  • 项目的拥有者合并或关闭你的合并请求

:eyes:

Wiki

wiki页面的制作。

有待了解的知识点

列个表,自己还不懂的:

  • 以补丁形式的Merge
  • Git服务器搭建

未完成:配图片。实际的pul request操作