首页 > 技术文章 > git使用

zealousness 2018-04-08 22:12 原文

 

 

git工作流

http://www.ruanyifeng.com/blog/2015/08/git-use-process.html 

 

git 相关工具

在 vscode 中可以很方便的使用的 git 插件,可以看到每一行代码的修改历史:gitLens

 

 

git cherry-pick

根据commit id,将另一个分支上的commit 引入到你 当前分支 上

 https://blog.csdn.net/fightfightfight/article/details/81039050

 git cherry-pick 教程 - 阮一峰的网络日志 (ruanyifeng.com)

 

git pull 与 git pull --rebase

git pull 相当于 git fetch+git merge,有可能产生merge commit

git pull --rebase 相当于 git fetch+git merge,不会产生merge commit

所以更推荐使用

git pull --rebase

但两者都需要解决confict。

 

git rebase -i master

将master分支的更新rebase到当前分支下(即改动的是当前分支),-i 代表交互模式,可以手动定制哪些commit要,哪些不要

 

git 忽略文件 gitignore 与 exclude

https://blog.csdn.net/stelalala/article/details/10339023

https://blog.csdn.net/Vic___/article/details/9446729

区别,gitignore会被推上去,exclude不会 

 

添加exclude的方法:

vim .git/info/exclude

在该文件中添加要exclude的文件在项目目录下的相对路径的正则式,保存即可

 

 

git reset && git reset --soft && git reset --hard

git reset --hard 把commit的内容直接丢弃掉

git reset --soft  把commit的内容恢复到暂存区(变成绿色字)

git reset           把commit的内容恢复到工作区(变成红色字)

 

git取消reset

一不小心git reset了,现在想取消reset,恢复原来的commit:

git reflog 查看操作历史,找到之前 HEAD 的 hash 值,然后 git reset --hard 到那个 hash 即可。

 

 

git撤销所有的绿字并且都变成红字

先git commit 再 git reset HEAD^

git撤销上一步add的绿字并且变成红字

git rm --cached

 

git rm

 功能同git add,工作区文件的删除提交到暂存区(跟add功能相同,只不过add不能用来指名add被删除的文件,所以有git rm

 

git

 

个人觉得理解git最关键在理解分支
首先要理解一个东西叫当前分支
git branch可以查看本地库所有分支,前面有*号的就是当前分支,工作区中显示的内容就是当前分支的内容,所有的操作都是针对当前分支的

 

合幷分支:

https://backlog.com/git-tutorial/cn/stepup/stepup2_7.html

https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001375840202368c74be33fbd884e71b570f2cc3c0d1dcf000

现在有一个旧的分支A,要把新分支B的功能给它。

切换到分支A,执行

git merge B

 

看到如下结果表示merge部分成功,myfile.txt中有冲突待解决

$ git merge issue3
Auto-merging myfile.txt
CONFLICT (content): Merge conflict in myfile.txt
Automatic merge failed; fix conflicts and then commit the result.

 

打开myfile.txt,手动修改解决冲突(冲突处已经用<<<<<<<<<标记出来了,如下)

<<<<<<< HEAD
content A
=======
content B
>>>>>>> B

 

修改完成之后 git add . 然后再 git commit 就完成了merge

 

删除本地有但远程库已经不存在的分支的追踪链接

git prune remote

 

 

git设置

git config --global xxx

例如,设置只push当前分支

git config --global push.default current

 

 

项目克隆

大文件要安装git lfs

git lfs安装:https://www.jianshu.com/p/493b81544f80

curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bashsudo apt-get install git-lfs
git lfs install

有子模块clone的时候要--recursive

 

 删除远程branch和tag

branch和tag区别:https://www.zhihu.com/question/28784805

在Git v1.7.0 之后,可以使用这种语法删除远程分支:

$ git push origin --delete <branchName>
删除tag这么用:

git push origin --delete tag <tagname>



git 1.7.0之前,可以使用这种语法,推送一个空分支到远程分支,其实就相当于删除远程分支: git push origin :<branchName> 这是删除tag的方法,推送一个空tag到远程tag: git tag -d <tagname> git push origin :refs/tags/<tagname> 两种语法作用完全相同。

 

 

合并两个各有新修改的分支 git merge(后面讲) && git rebase(推荐)

git rebase

https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%8F%98%E5%9F%BA

https://backlog.com/git-tutorial/cn/stepup/stepup1_5.html

原分支情况:

 

rebase操作:

 rebase常见的应用场景:

例如,在开发功能的topic分支操作途中,需要修改bug。

在开发功能的主题分支操作的途中,需要进行错误的修改

这时,merge分支还是处于开发功能之前的状态。在这里新建修改错误用的主题分支,就可以从开发功能的作业独立出来,以便开始新的工作。

新建修改用的主题分支,可以从开发功能独立出来开始操作

完成bug修正的工作后,把分支导入到原本的merge分支后就可以公开了。

导入到原本的合并分支後就可以公开

回到原本的分支继续进行开发功能的操作。

回到原本的分支继续进行开发功能的操作

但是,如果要继续进行操作,你会发现需要之前修正bug时提交X的内容。有2种导入提交X的内容的方法:一种是直接merge,另一种是和rebase导入提交X的合并分支。

这里我们使用rebase合并分支的方法。

rebase到合并分支

在导入提交X的内容的状态下继续进行开发功能。

 

merge操作:

更新并解决冲突并产生merge commit

git pull

如果有冲突会自动产生一个merge commit,并且git会提示你为这个merge commit填写一些备注:"please enter a commit message to explain why this merge is necessary, especially if it merges an updated upstream into a topic branch."

填写备注时:

1、填写一些信息

2、CTRL + X

3、CTRL + C

4、It will ask you to save file, Press Y, then you are done.

 

更新并解决冲突并且不产生merge commit

git pull --rebase

https://www.cnblogs.com/wangiqngpei557/p/6056624.html

 

 

git checkout: 恢复已修改的内容

如何部分恢复—— git checkout --patch -- .

To elaborate on Matt's answer, git checkout --patch -- <path argument> starts an interactive mode with the following options:

y - stage this hunk
n - do not stage this hunk

q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk

s - split the current hunk into smaller hunks
e - manually edit the current hunk

? - print help

The y n s and e options are a good place to start.

See also:

 

git checkout: 获取本地没有的远程分支(注意不是直接git checkout!)

git checkout -b 本地分支名 origin/远程分支名

 或者使用-t参数,它默认会在本地建立一个和远程分支名字一样的分支
git checkout -t origin/xcomm_zynq_4_4

https://www.cnblogs.com/phpper/p/7136048.html

https://www.cnblogs.com/nanopeng/p/7018734.html

https://zhidao.baidu.com/question/264071541339121645.html

注意,直接git checkout origin/远程分支名是不行的!https://segmentfault.com/q/1010000009561825

 

本地创建新分支

首先,我们创建dev分支,然后切换到dev分支:

$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

 

 

git push

https://www.yiibai.com/git/git_push.html

http://www.cnblogs.com/springbarley/archive/2012/11/03/2752984.html

 

git push origin test:master         // 提交本地test分支作为远程的master分支 //好像只写这一句,远程的github就会自动创建一个test分支
git push origin test:test              // 提交本地test分支作为远程的test分支

 

直接git push有时会有warning提示并且无法push——https://blog.csdn.net/daijingxin/article/details/51326715

$ git push origin master

上面命令表示,将本地的master分支推送到origin主机的master分支。如果master不存在,则会被新建。

如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。

$ git push origin :master
# 等同于
$ git push origin --delete master

上面命令表示删除origin主机的master分支。如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。

$ git push origin
上面命令表示,将当前分支推送到origin主机的对应分支。如果当前分支只有一个追踪分支,那么主机名都可以省略。
$ git push
如果当前分支与多个主机存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数使用git push
$ git push -u origin master
上面命令将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。

不带任何参数的git push,默认只推送当前分支,这叫做simple方式。此外,还有一种matching方式,会推送所有有对应的远程分支的本地分支。Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。如果要修改这个设置,可以采用git config命令。

$ git config --global push.default matching
# 或者
$ git config --global push.default simple

 

 查看HEAD指针指向的commit的id

git rev-parse HEAD

 

 

查看区别 git diff

https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/diffs

git diff                                       查看尚未暂存的文件更新了哪些部分

git diff filename 查看尚未暂存的某个文件更新了哪些

git diff –cached                    查看已经暂存起来的文件和上次提交的版本之间的差异

git diff –cached filename 查看已经暂存起来的某个文件和上次提交的版本之间的差异

git diff ffd98b291e0caa6c33575c1ef465eae661ce40c9 b8e7b00c02b95b320f14b625663fdecf2d63e74c 查看某两个版本之间的差异

git diff ffd98b291e0caa6c33575c1ef465eae661ce40c9:filename b8e7b00c02b95b320f14b625663fdecf2d63e74c:filename 查看某两个版本的某个文件之间的差异

 

一对文件的差别

因为可能有多对文件差别,因此有多个如上图所示的大块 

 

暂存工作区的修改并恢复到最近一次commit

git stash

保存当前工作进度,会把暂存区和工作区的改动保存起来。执行完这个命令后,在运行git status命令,就会发现当前是一个干净的工作区,没有任何改动。使用git stash save 'message...'可以添加一些注释

git stash list

显示保存进度的列表。也就意味着,git stash命令可以多次执行。

git stash pop [–index] [stash_id]

通过git stash pop命令恢复进度后,会删除当前进度。

git stash apply [–index] [stash_id]

除了不删除恢复的进度之外,其余和git stash pop 命令一样。

git stash drop [stash_id]

删除一个存储的进度。如果不指定stash_id,则默认删除最新的存储进度。

git stash clear

删除所有存储的进度。
 
 

清空工作区的修改(撤销编辑)

git checkout .

git checkout 文件名

https://segmentfault.com/q/1010000005850900

 

 撤销暂存区的修改(撤销add)

git reset HEAD

git reset HEAD 文件名

(或者直接stash之后再将栈中的内容扔掉——https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%82%A8%E8%97%8F%E4%B8%8E%E6%B8%85%E7%90%86)

 

撤销本地库的修改(撤销commit)

有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以使用 --amend 选项重新提交:

$ git commit --amend

此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行此命令的话,相当于有机会重新编辑提交说明,但将要提交的文件快照和之前的一样。

启动文本编辑器后,会看到上次提交时的说明,编辑它确认没问题后保存退出,就会使用新的提交说明覆盖刚才失误的提交。

如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交:

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

上面的三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。

 

 

撤销远程库的修改(即删除远程库最新的一个commit)

讲要撤销的commit所在的分支checkout到本地

在本地reset

然后push上去

注意,这算是一个比较危险的操作

 

clone下来之后,初始化并更新submodule

参考:https://www.jianshu.com/p/f8a55b972972

git submodule update --init --recursive

为什么要用submodule:对于一些比较大的工程,为了便于复用,常常需要抽取子项目。例如题库客户端现在包括3门考试,客户端涉及的公共UI、公共底层逻辑、公共的第三方库、以及公共的答题卡扫描算法就被我分别抽取成了子项目。这些子项目都以git submodule的形式,增加到工程中

git submodule的坑:

 修改submodule:

默认git submodule update并不会将submodule切到任何branch,所以,默认下submodule的HEAD是处于游离状态的(‘detached HEAD’ state)。所以在修改前,记得一定要用git checkout master将当前的submodule分支切换到master,然后才能做修改和提交。

更新submodule:

git submodule update [通过name指定某一个submodule,如果不指定,默认对所有submodule进行操作] --recursive

git submodule支持的命令有限,例如log之类的命令都不支持,因此我们也可以cd到submodule的目录中,然后当作正常的库直接git操作

https://blog.devtang.com/2013/05/08/git-submodule-issues/

 http://mobile.51cto.com/aprogram-393324.htm

 No submodule mapping found in .gitmodules for path错误解决办法:

进入父项目的目录下。ctrl+H显示出隐藏文件,然后修改.gitmodule文件,添加上如下两行

[submodule "submodule名"]
    path = submodule目录在父项目根目录下的相对路径
    url = submodule库的url

例如:
[submodule "homebrew"]
    path = test/support/homebrew
    url = https://github.com/mxcl/homebrew.git
 

 

 

查看本地修改状态

git status
-s查看简短信息

修改提交到本地暂存区

git add

查看历史版本

git log
一个好看的log形式:
git log --oneline --graph --decorate
为上面这个长命令指定一个别名
git config alias.lg "log --oneline --graph --decorate"

 

本地暂存区提交到本地库

git commit
-m 指定附加信息
注意:直接使用git commit -am可以替代git add+git commit -m的功能

 

回退到当前版本的上一版本

git reset --hard HEAD^

HEAD 这是当前分支版本顶端的别名,也就是在当前分支你最近的一个提交

reset

如果我们执行:

git reset HEAD

任何事情都不会发生,这是因为我们告诉GIT重置这个分支到HEAD,而这个正是它现在所在的位置。

git reset HEAD~1

git reset HEAD^

当我们再执行上面的命令时(HEAD~1是“the commit right before HEAD”的别名,或者说:put differently "HEAD's parent"),我们的分支将会如下所示

 

HEAD^指当前版本的上一版本

#注意如果是CMD下的话^符号要加双引号

 

  • Parameters
  1. soft

--soft参数告诉Git重置HEAD到另外一个commit,但也到此为止。如果你指定--soft参数,Git将停止在那里而什么也不会根本变化。这意味着index,working copy都不会做任何变化,所有的在original HEAD和你重置到的那个commit之间的所有变更集都放在stage(index)区域中。

 

  2.hard

--hard参数将会blow out everything.它将重置HEAD返回到另外一个commit(取决于~12的参数),重置index以便反映HEAD的变化,并且重置working copy也使得其完全匹配起来。这是一个比较危险的动作,具有破坏性,数据因此可能会丢失!如果真是发生了数据丢失又希望找回来,那么只有使用:git reflog命令了。makes everything match the commit you have reset to.你的所有本地修改将丢失。如果我们希望彻底丢掉本地修改但是又不希望更改branch所指向的commit,则执行git reset --hard = git reset --hard HEAD. i.e. don't change the branch but get rid of all local changes.另外一个场景是简单地移动branch从一个到另一个commit而保持index/work区域同步。这将确实令你丢失你的工作,因为它将修改你的work tree!

  3.mixed(default)

--mixed是reset的默认参数,也就是当你不指定任何参数时的参数。它将重置HEAD到另外一个commit,并且重置index以便和HEAD相匹配,但是也到此为止。working copy不会被更改。所有该branch上从original HEAD(commit)到你重置到的那个commit之间的所有变更将作为local modifications保存在working area中,(被标示为local modification or untracked via git status),但是并未staged的状态,你可以重新检视然后再做修改和commit

 

 

G   H   I   J
 \ /     \ /
  D   E   F
   \  |  / \
    \ | /   |
     \|/    |
      B     C
       \   /
        \ /
         A

A =      = A^0
B = A^   = A^1     = A~1
C = A^2  = A^2
D = A^^  = A^1^1   = A~2
E = B^2  = A^^2
F = B^3  = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2  = B^^2    = A^^^2  = A~2^2
I = F^   = B^3^    = A^^3^
J = F^2  = B^3^2   = A^^3^2

 

 回退到指定版本

通过git log --graph查看想要回到的版本

然后git reset 版本id前六位 回到该版本

 

查看有哪些分支

git branch
-r查看远程 -a查看所有


创建新分支(默认在当前分支基础上创建,创建后不切换到新分支)

git branch 新分支名字

 

切换当前分支

git checkout 切换到的分支名字

创建新分支并切换到新分支(上面两条命令的结合)
git checkout -b 新分支名字


合并分支:将其他分支融合到当前分支中(改变的是当前分支)
git merge 要合到当前分支的分支的名字
(两个分支A,B,如果分支A中存在分支B没有的内容,而分支B中不存在分支A没有的内容(分支A真包含分支B),则合并结果为分支A,不论A是当前分支还是合并到当前分支的分支)
冲突解决:
合并后如果出现冲突,git会要求你解决冲突后才能checkout到别的分支去(目前能想到的解决冲突的方法是手动改或reset)

合并分支默认是fast forward模式,也就是直接把当前分支的指针指向要合并到当前分支的分支,但不是所有的情况下都适合用fast forward的,后续学习到会继续更新

 

删除分支:

git branch -d 要删除的分支名字


远程克隆到本地:
git clone
-b 指定单独克隆某一个分支

远程更新到本地:
git pull/git fetch(git pull=git fetch+git merge)

本地提交到远程:
git push


2、开发流程

给你一个项目的时候
从远端将项目clone下来
一般地,clone下来的项目里面的分支有master,develop,和其他开发者的feature

在本地创建你自己的feature,基于develop

添加你自己的功能,commit到本地分支,把修改push到远端……
feature的过程中:
写对了,则commit,然后push到远端(最好经常push一下正确的代码)
写错了,则commit,然后删除本地的feature分支,然后将远端的feature分支pull下来(或fetch下来merge),实现回退到上一版本
不要轻易用reset,容易有一些奇奇怪怪的错误。

你的feature写完,测试可以运行之后,就要在本地把feature和develop融合了,在这之前先pull更新一下develop,然后将feature finished,将feature与develop融合并删除feature分支
将新的develop push到远端,新功能完成。

3、流程
1)使用ssh登陆git远程服务器

linux下:
ssh-keygen -t rsa -C "zss1@meitu.com"
然后会告诉你生成的私钥文件名和公钥文件名,并问提示你公钥私钥存放的路径,不用管直接回车
然后cd ~/.ssh文件夹下根据上一行告诉你的文件名找到后缀为.pub的公钥文件,将文件内容复制到git远程服务器中指定位置

SSH协议使用了RSA加密算法
RSA加密算法是一种非对称加密算法

https中使用了非对称加密:
非对称加密,是为了保证公钥持有者(用户)向私钥持有者(服务器)发送消息时,不被其他公钥持有者(其他用户)劫持消息并解密
公钥和私钥都在服务器端生成,生成后公钥被公开发送给用户
然后用户就可以用公钥加密自己想要单独传给服务器而不被其他用户破译的消息了
https中,用户想要发送的不被其他用户破译的消息其实就是用户和服务器之间使用的对称加密的密钥,在用户处使用随机数生成

那么git为什么要使用非对称加密?
实际上是使用了“公钥登陆”方法,免除了每次pull,push都要登陆的麻烦
所谓"公钥登录",就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。
这种方法要求用户必须提供自己的公钥。如果没有现成的,可以直接用ssh-keygen生成一个

2)clone到本地
git clone 远程仓库地址
如果要单独克隆某个分支:
git clone -b 要克隆的分支名 远程仓库地址

3)branch查看分支
一个没有任何提交的项目,克隆下来后用git branch查看是看不到任何分支的
因为分支branch实际上是指向某次提交的指针,如果一个项目连一次提交都没有,那么也就没有任何分支
git会在第一次提交时自动创建一个master分支(指针)指向第一次提交

4)给git命令指定一个别名
git config alias.st status
status命令指定一个别名st,然后接下来你就可以这样查看工作区状态了——git st

4、smartgit使用
smartgit中打开本地库:
repository->add or create 或 clone

创建master-release/hotfix-develop-feature工作流:
Branch->Git Flow->Configure(弹出的对话框选Full,确定)

在工作流中创建新的分支:
Git Flow图标->start feature/start hotfix/start release

feature写完了,合并到develop并删除该feature:
Git Flow图标->finish feature(弹出对话框后选第一种,确定)

推荐阅读