Git 操作及原理

Git Diff 的工作原理

Myers差分算法

创建基于某个 commit id 的分支

1
git checkout -b dev c99d6500

查看指定分支的提交

1
2
git log master
git config --global alias.nicelog "log --graph --abbrev-commit --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'"

如下所示:

git 如何存储

git 如何 merge

git 如何 rebase

是否能 cherry-pick 所有的 commit

【转载-摘要】彻底搞懂 Git-Rebase

原文链接:http://jartto.wang/2018/12/11/git-rebase/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

rebase的主要功能就是改变commit历史,在这篇文章中,场景一是改变未push的commit历史,场景二是在合并分支时,改变当前分支的历史,从而达到简化commit历史的目的,让历史一目了然。对强迫症患者来说简直就是福音。

Rebase 场景一:如何合并多次提交纪录?

1.我们来合并最近的 4 次提交纪录,执行:
git rebase -i HEAD~4
2.这时候,会自动进入 vi 编辑模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
s cacc52da add: qrcode
s f072ef48 update: indexeddb hack
s 4e84901a feat: add indexedDB floder
s 8f33126c feat: add test2.js
# Rebase 5f2452b2..8f33126c onto 5f2452b2 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#

有几个命令需要注意一下:

1
2
3
4
5
6
7
p, pick = use commit
r, reword = use commit, but edit the commit message
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
f, fixup = like “squash”, but discard this commit’s log message
x, exec = run command (the rest of the line) using shell
d, drop = remove commit

按照如上命令来修改你的提交纪录:

1
2
3
4
s cacc52da add: qrcode
s f072ef48 update: indexeddb hack
s 4e84901a feat: add indexedDB floder
p 8f33126c feat: add test2.js

3.如果保存的时候,你碰到了这个错误:
error: cannot 'squash' without a previous commit
注意不要合并先前提交的东西,也就是已经提交远程分支的纪录。

4.如果你异常退出了 vi 窗口,不要紧张:

git rebase --edit-todo
这时候会一直处在这个编辑的模式里,我们可以回去继续编辑,修改完保存一下:

git rebase --continue
5.查看结果

git log
在这里插入图片描述
三次提交合并成了一次,减少了无用的提交信息。

Rebase 场景二:分支合并

1.我们先从 master 分支切出一个 dev 分支,进行开发:

git:(master) git checkout -b feature1git1
2.这时候,你的同事完成了一次 hotfix,并合并入了 master 分支,此时 master 已经领先于你的 feature1 分支了:
git2
3.恰巧,我们想要同步 master 分支的改动,首先想到了 merge,执行:

git:(feature1) git merge master
git3
图中绿色的点就是我们合并之后的结果,执行:

git:(feature1) git log
就会在记录里发现一些 merge 的信息,但是我们觉得这样污染了 commit 记录,想要保持一份干净的 commit,怎么办呢?这时候,git rebase 就派上用场了。

4.让我们来试试 git rebase ,先回退到同事 hotfix 后合并 master 的步骤:
git4
5.使用 rebase 后来看看结果:

git:(feature1) git rebase master
这里补充一点:rebase 做了什么操作呢?

首先,git 会把 feature1 分支里面的每个 commit 取消掉;
其次,把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下;
然后,把 feature1 分支更新到最新的 master 分支;
最后,把上面保存的 patch 文件应用到 feature1 分支上;
git5
commit 记录我们可以看出来,feature1 分支是基于 hotfix 合并后的 master ,自然而然的成为了最领先的分支,而且没有 mergecommit 记录,是不是感觉很舒服了。

6.在 rebase 的过程中,也许会出现冲突 conflict。在这种情况,git 会停止 rebase 并会让你去解决冲突。在解决完冲突后,用 git add 命令去更新这些内容。

注意,你无需执行 git-commit,只要执行 continue

git rebase --continue
这样 git 会继续应用余下的 patch 补丁文件。

7.在任何时候,我们都可以用 –abort 参数来终止 rebase 的行动,并且分支会回到 rebase 开始前的状态。

git rebase —abort

不足

只要你的分支上需要 rebase 的所有 commits 历史还没有被 push 过,就可以安全地使用 git-rebase来操作。

因为push到远程仓库后,你再次提交时,git对比会发现两个仓库的历史不一致。

git中的回车换行符CRLF与LF

设置core.autocrlf

假如你正在Windows上写程序,又或者你正在和其他人合作,他们在Windows上编程,而你却在其他系统上,在这些情况下,你可能会遇到行尾结束符问题。这是因为Windows使用回车和换行两个字符来结束一行,而Mac和Linux只使用换行一个字符。虽然这是小问题,但它会极大地扰乱跨平台协作。

true

Git可以在你提交时自动地把行结束符CRLF转换成LF,而在签出代码时把LF转换成CRLF。用core.autocrlf来打开此项功能,如果是在Windows系统上,把它设置成true,这样当签出代码时,LF会被转换成CRLF:
$ git config --global core.autocrlf true

input

Linux或Mac系统使用LF作为行结束符,因此你不想 Git 在签出文件时进行自动的转换;当一个以CRLF为行结束符的文件不小心被引入时你肯定想进行修正,把core.autocrlf设置成input来告诉 Git 在提交时把CRLF转换成LF,签出时不转换:
$ git config --global core.autocrlf input
这样会在Windows系统上的签出文件中保留CRLF,会在Mac和Linux系统上,包括仓库中保留LF。

false

如果你是Windows程序员,且正在开发仅运行在Windows上的项目,可以设置false取消此功能,把回车符记录在库中:
$ git config --global core.autocrlf false

来源

《git pro》

git使用笔记

记录自己在使用git中所用到的命令,算是半个笔记吧~

git merge & git rebase

参考

git初次运行时的配置

三个地方

  • 系统级别,/etc/gitconfig, 修改此选项时需要加上 --system选项
  • 当前用户级别,~/.gitconfig, 修改此选项时需要加上--global选项
  • 当前仓库级别,.git/config。
    低级别覆盖高级别的配置信息。

配置信息

  • 用户名 git config --global user.name "John Doe"
  • 用户邮箱 git config --global user.email [email protected]
  • 编辑器 git config --global core.editor vim

查看信息

  • 查看所有配置信息 git config --list
  • 查看某项配置信息 git config user.name

忽略文件

.gitignore文件忽略不想进行版本控制的文件。
参考https://github.com/github/gitignore

命令的详情

git add

三个作用:

  • 添加追踪
  • 添加到暂存区
  • 标记冲突文件状态为已解决

git status -s

如果嫌弃不带-s的命令输出的信息太繁杂,那么可以使用这个。
新添加的未跟踪文件前面有 ?? 标记,新添加到暂存区中的文件前面有 A 标记,修改过的文件前面有 M 标记。
出现在右边的 M 表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。

查看修改

git diff 查看尚未暂存的文件更新了哪些部分
git diff --staged 查看已暂存的将要添加到下次提交里的内容

提交更新

git commit

-m 添加一段信息,作为提交说明
-a 跳过暂存区,直接将已追踪的文件暂存起来并提交
–amend 此次提交的结果替代上次提交的结果

删除文件

git rm

从已跟踪文件清单中移除(确切地说,是从暂存区域移除),并连带从工作目录中删除指定的文件
-f 删除之前修改过并且已经放到暂存区域的文件
–cached 把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中

移动文件(或重命名)

git mv
等价于

1
2
3
mv 
git rm
git add

查看提交

git log

-p 显示每次提交的差异
-p -2 显示最近两次的差异
–stat 每次提交的简略的统计信息
–graph 显示 ASCII 图形表示的分支合并历史。
–pretty使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和format(后跟指定格式)。

移除暂存文件

git reset HEAD <file>... 将文件从暂存区移除

撤销文件的修改内容

git checkout -- <file>... 将此文件做的文件全部撤销

使用总结

git基本命令

git init 在执行命令的目录下创建git仓库

git add * 添加所有的文件到缓存区

git commit * -m 提交所有追踪的文件到git仓库

git pull origin master 从远程仓库拉代码到本地git仓库

git push origin master 将自己的代码推送到远程git仓库

创建新的分支

git branch 显示所有的分支

git branch -a 显示所有的分支包括远程的分支

git checkout -b branch_name 创建branch_name分支并切换到该分支上

git checkout branch_name 切换到branch_name分支上

关于.gitignore文件

这个文件顾名思义是起到忽略作用的,在git仓库中使用此文件,将不需要添加进git仓库的文件排除在外。可是在使用的时候,会遇到向其中添加了文件名,却
不起作用的情况。很奇怪,参考网上的说法,当已经添加该文件进入git仓库后,再在.gitignore中除去该文件,就会遇到这种情况,我就是属于这种情况,这时需要将其从仓库中删除,然后再执行git add时就会忽略掉该文件..gitignore只对未追踪的文件有过滤效果。可参考如下代码

1
2
3
git rm -r --cached .
git add .
git commit -m "comment"

关于本地分支与远程分支的链接关系

当从远程仓库上面拉下代码之后,其中有若干分支,如若想在本地建立一个分支,并使之与远程分支中的某个分支对应,那么该如何操作呢?

git checkout --track origin/branch_name local_branch_name 这个命令会自动创建local_branch_name,如果它已经存在了,那么将执行失败~

查看、删除远程仓库分支/Tag

试了试将所有的feature分支都推送到远程仓库,后来发现那个分支基本上没啥用,在将feature分支merge到dev分支上后,feature分支就一直处于当初的那个状态,当dev一直向前走的时候,你再次回到该feature分支,相当于回到了dev分支之前的某个节点,因此我认为将其推送到远程仓库是没有多大的意义的,所以动起了删掉所有本地和远程仓库中已merge的feature分支。

首先是查看远程仓库里面所含有的feature分支:

1
2
3
4
5
6
7
8
9
asahi@asahis-MBP  ~/AndroidStudioProjects/NHKNews   master  git branch -a
dev
feature-remote
* master
remotes/origin/HEAD -> origin/master
remotes/origin/dev
remotes/origin/feature-main-page
remotes/origin/feature-remote
remotes/origin/master

当使用git branch -d feature-main-page之后,得到的结果如上,可是我只是删除了本地的分支,远程的分支依然还在,该如何删除这个远程仓库里面的分支呢?有git命令为git push origin --delete origin/feature-main-page,可是出现错误,不能删除,因此试了另外一个命令git push origin :feature-main-page,成功删除。然后对于feature-remote分支,直接使用前条命令,可将本地与远程仓库里面的分支一起删除.

再次使用git remote show origin,查询得到的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
* remote origin
Fetch URL: [email protected]:xuchuanjun/NHKNews.git
Push URL: [email protected]:xuchuanjun/NHKNews.git
HEAD branch: master
Remote branches:
dev tracked
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local refs configured for 'git push':
dev pushes to dev (up to date)
master pushes to master (up to date)

总结:超强的总结