git 沙河游戏节点图, 自由沙盒模拟git, 各种交互git命令

 


git学习练习总资源连接: https://try.github.io/ (练习已通,有document)javascript

本沙盒游戏教学:https://learngitbranching.js.org/?demo java

自由沙盒模拟网页 : http://git-school.github.io/visualizing-git/git

 

好的译文: https://github.com/geeeeeeeeek/git-recipes/wikigithub


 

什么是git? 

一个分布式的源代码库。管理Linux内核源代码。正则表达式

 git已快照形式保存和处理内容,每个提交都是一次快照。git能够在快照之间回滚。vim

 


 

一个节点表明一个commit.api

*表明当前分支的最后一次提交:HEAD缓存

master是主干。安全

其余名字是分支。 并发

 

git merge :用于合并分支的代码。

git rebase : 线性合并分支:

 

git rebase [-i] [目标] [移动记录] 

git rebaes [目标]  #省略[要移动的记录],则为当前分支的全部commit。

 

假如当前分支是bugFix:

  1. git rebase master. 这样bugFix分支就至关于在master的基础上新增的代码了。
  2. git checkout master 回到master
  3. git rebase bugFix,  master和bugFix的代码都同样了。

 

HEAD:

是一个对当前检出记录的符号引用 -- 也就是指向你正在其基础上进行工做的提交记录

它老是指向当前分支上最后一次的提交记录。 大多数提交树的git命令都是从改变HEAD的指向开始的。

⚠️,后面章节讲的远程分支 origin/master是例外

 

HEAD 一般是指向分支名的(如bugFix)。在你提交commit时,改变了分支的状态,这一变化经过HEAD变得可见。

 

分离的HEAD:

让它指向某个具体的提交记录(hash值)而不是分支名。 

git checkout <hash> 

git checkout命令本质就是移动HEAD,到目标commit点, 而后更新工做目录以匹配这个commit点。

由于这个操做会overwrite local changes,致使改变的文件丢失,因此Git强迫你先commit或stash工做目录中的改变的文件。

⮀ git checkout master error: Your local changes to the following files would be overwritten by checkout: app/assets/javascripts/search.js Please commit your changes or stash them before you switch branches.

 


 

关于git stash  (具体工做原理和所有的知识见连接文章)

会把还没有加入stage的文件和statge中的文件保存(就是未commited的文件),以便在以后使用。

以后能够revert them from your working copy.

 

如今能够进入任何其余操做,如建立新commits, 转变分支,执行其余git操做了。

⚠️stash是本地的。当你push的时候,stash不会被传输。

 

Re-applying your stashed changes:

$ git stash pop

 

另外使用git stash apply, 能够reapply the changes的同时在stash中保留它们。这在为多个分支应用时有用。

⚠️:默认Git不会stash 未tracked文件和ignored files。

 

 

 


 

相对引用 

 

经过指定提交记录hash值的方式在Git中移动不方便操做。

必须用到git log, 并且hash值很是长。

所以能够只使用前几个字符表明一个提交记录 , 即“相对引用”。

^    表👆向上移动一个commit记录。

~2 表明向上移动2个提交记录,~5,表明移动5个提交记录

使用git checkout HEAD^, 就表明向上移动一次。

 


 

强制修改分支位置--移动分支

 

git branch -f master HEAD~3

表明把master向上移动三个提交节点,即第3个father note 

-f :表明--force, force creation, move/rename, deletion 

 


 

撤销变动

  • git reset
  • git revert 

 

Command Scope Common use cases
git reset Commit-level Discard commits in a private branch or throw away uncommited changes
git reset File-level Unstage a file
git checkout Commit-level Switch between branches or inspect old snapshots
git checkout File-level Discard changes in the working directory
git revert Commit-level Undo commits in a public branch
git revert File-level (N/A)

 

Git Reset

原文:

takes a specified commit and resets the "three trees" to match 
the state of the repository at that specified commit.

Three Trees

 

把分支回退指定个数的commit记录,来实现撤销改动。至关于使用时间机器回退到过去开发的阶段。(撤销的提交记录还存在,只是未加入stage暂存区) 

⚠️ 这条命令对团队使用的远程分支无效!

例子:

# 从当前工做目录回退一个commit.
git reset HEAD~

# 从当前工做目录回退2个commit
git reset HEAD~2 

⚠️被回退的2个commit提交变成悬挂提交。下次Git执行垃圾回收时,这两个提交会被删除!

 

git reset能够把stage中的文件拿出stage。git reset </filename>

⭠ autoquery± ⮀ git reset README.md Unstaged changes after reset: M README.md

 

 

三个模式选项

  • --soft -stage缓存区和工做目录都不会改变。
  • --mixed默认选项。✅
    • 缓冲区和你指定的提交同步a(在提交a后,加入缓存区的文件被拿出来了)
    • 工做目录不受影响(在提交a后对工做目录中的文件进行的改变被保留!)。
  • --hard -缓存区和工做目录都被更新,以匹配指定的commit点a。
    • 至关于回退到刚刚提交完a的状态! 
    • 在提交a后的操做所有删除,包括工做目录中对文件的改变,至关于时光倒流

能够认为这个三个模式是对a git reset操做的做用域的定义!

通常使用默认的--mixed。

 


 

Git Revert (点击见详细)

专门用于撤销远程提交记录。但本质上是新增一个commit记录,但更改了code,去掉了以前那个commit记录中的变动代码。

c0-c1-c2(->c3)

git revert C1, 结果是新增了一个c3. 

c3是c1的反转操做,即c1中变化的代码,在c3中被撤销了。

 

例如, 你追踪一个bug并发现它是在某个commit点a内增长的一个变量。你无需手动进入这个commit点,删除这个变量,而后再committing一个新的snapshot,你直接使用git revert a命令自动为你作上面的事情。 

⚠️:若是你revert提交点c1, 可是c2中有对c1中变化的代码的进一步修改,你不能使用git revert c1, 系统会提示你冲突,须要先搞定冲突代码。

 

How it works

git revert命令用于撤销一个仓库的commit历史中的某个变化。

其余撤销命令如git checkout 和 git reset,移动HEAD和branch ref pointers到一个指定的commit点。

Git revert也take a specified commit,可是,git revert不会移动ref pointers到这个commit点。

而是执行一个反转操做,反转那个commit的改变的代码,并建立一个新的“revert commit”。

最后ref pointers 会更新,指向这个新的"revert commit", 让这个commit成为分支的端点tip。

 

选项

-e  --edit (默认选项) 打开系统的编辑器,提示你编辑commit信息。

-n --no-commit (一个特别的选项)使用它,git revert不会建立新的commit, 只会在working directory反转变化的代码,并在缓存中加入反转代码后的文件。一句话理解:须要你手动提交!其余没变化。

 

和get reset的比较:

1:不会改变历史记录。

  • 基于这个优势git revert能够在一个public branch上使用,
  • 而git reset最好在我的的branch上进行操做。

2:git revert只改变单一的提交点。而git reset会从当前提交往回退(回到过去)。

能够这么理解:

  • git revert是撤销committed changes的工具

⚠️git revert也和git checkout相似,在revert操做期间会重写工做目录中的文件。因此它会要求你commit/stash changes。

 


 

 

File-level Operations

git reset和git checkout命令能够接收一个file path做为参数。这会强制把它们的操做限制到一个单一文件。

例子:

git reset HEAD~2 foo.py

 

当引用一个文件路径时, git reset更新缓存区以匹配特定commit点的版本的指定文件。

👆的命令会取得在提早两个提交点的版本的foo.py文件,并把它放入Staged files缓存区中。

可是工做区出现对应的文件的unstaged 状态:

⭠ autoquery ⮀ git reset head~3 README.md Unstaged changes after reset: M README.md ⭠ autoquery± ⮀ git status On branch autoquery Changes to be committed: (use "git reset HEAD <file>..." to unstage)  modified: README.md Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory)  modified: README.md

 

个人理解:

  1. 执行git reset head~2 foo.py命令
  2. 取得的文件foo.py放入缓存区,但程序发现取出的文件和当前的foo.py文件的内容不一致,因此出现👆的状况。

另外,若是我修改了上一个commit点的foo.py文件并放入缓存区。

而后又执行git reset head~2 foo.py

程序发现取出的文件和当前修改的foo.py文件的内容不一致,会把当前修改的foo.py文件放入工做目录区。

⚠️复杂的操做尽可能配合可视辅助工具Sourcetree

 

 

Git checkout head File命令

它会把file放入working directory工做目录。

⚠️个人实际操做是使用此条命令后,取出的文件会放入到缓存区!个人理解:

  1. git checkout head File
  2. 程序自动把取出的文件执行git add命令,放入缓存区。
git checkout HEAD~2 foo.py

 

⚠️此时git checkout命令不会移动HEAD标签,即你不会切换分支!

⚠️:若是把这个变化git commit就至关于直接执行一个git revert命令了!!

 

 

 

 

 


 

总体提交记录 

当开发人员说,我想把这个commit放到这里,那个commit放到刚才的提交的后面,就可使用:

将一些commit复制到当前位置HEAD下面的话,使用这个命令: 

git cherry-pick <提交号>...

 

⚠️我的理解,是每一个commit应该是一个独立的模块。

⚠️,前提是你知道你想要的commit记录的hash值,才行。若是不知道,往下看⬇️

 

 


 

 

交互式的rebase 

 

git rebase -i HEAD~3

指带参数 --interactive的rebase命令,简写-i

 

rebase会打开一个UI界面:

 

 


 

git 技巧 1: 本地stage提交

 

假如当前在bugFix分支(C4), 而master在C1, 但愿只把C4合并到master上。

c1(master)-c2-c3-c4(bigFix*) 

第一种办法:

git checkout master#HEAD回到master 

git cherry-pick C4#ok了

第二种办法:

git rebase -i C1 #会打开UI界面,选择C4,去掉C2, C3 ,肯定。

#这时buFix是当前分支它包含C4直接放到了C1下面。

git checkout master #回到master, 由于master的数据比较旧

git merge bugFix#合并分支。 

 

 


 

git 技巧 2: 

 

git commit --amend

修复最新提交的便捷方式。做用是:

将缓存的修改和以前的commit合并到一块儿,生成一个新的提交并替换掉原来的提交。

这是从写项目历史的命令。

 

讨论:

仓促的提交在你平常开发过程当中时常会发生。很容易就忘记了缓存一个文件或者弄错了提交信息的格式。--amend 标记是修复这些小意外的便捷方式。

 

注意:⚠️

不要修复public commit! 永远不要重设和其余开发者共享的commit。

修复也同样:永远不要修复一个已经推送到公共仓库的commit!

 https://learngitbranching.js.org/?demo (点击连接看演示, 而后选择第四行第2个按钮)

c1(master)—c2(newImage)—c3(caption*) 

设计师须要在c2上调整图片格式,如何作?

|再问:

设计师干吗不在C3上调整?

答:猜想C2提交记录是针对图片的设计,c3提交记录是针对其余设计。

假设:

git checkout newImage

git commit --amend

git checkout capiton

git merge newImage #这会致使新产生一条commit记录。C4, 不符合线性的要求。

 

而使用:

# 调整C2, C3的位置,让C2位于分支tip, 由于git commit --amend用于最新提交点。
 git rebase -i C1 # 对C2进行修改
 git commit --amend #再调整回原先的结构。
 git rebase -i C1  #至关于,历史commit记录树没有发生变化。 

#而后就能够合并了
git checkout master
git checkout caption

 

 

⚠️,做者提示这可能会致使冲突:改用挑🍒,更快捷。cherry-pick

git checkout newImage

git commit --amend      #这会出现一个分叉。

git checkout master

git cherry-pick C2' C3

 


 

标签的做用:tag

 

给某个提交记录一个标签,相似⚓️。 用于重要版本的标记。 

git tag V1 <hash> 

若是不指定<hash>提交记录,则标记到HEAD指向的位置,所以能够写两条语法:

git checkout <hash>

git tag V1 

 

Git Describe

用来找到最近的tag。帮助你在commit record的历史中移动了屡次后找到方向。

git describe <ref>

<ref>是任何识别commit记录的引用,不指定的话,则以当前HEAD位置为准。

 

它的输出结构:

<tag>_<numCommits>_g<hash>

解释:

<tag>是离<ref>最近的标签,

numCommits是表示和<ref>相差多少个commit记录,

hash表示的是你所给定的<ref>所表示的提交记录hash值的前几位。 

 

Pushing tags

默认,标签不会自动推送上去。 --tags将你全部的本地标签推送到远程仓库。

git push <remoteName> <TagName>

push all tags:

git push <remoteName> --tags

 


 

挑战1:线性移动合并分支:git rebase  (能够看sandbox案例)

 

git rebase [-i] [目标] [要移动的记录] 

git rebaes [目标] #省略[要移动的记录],则为当前HEAD 


 

挑战2: 使用HEAD~和HEAD^2来移动HEAD的位置 

相对引用的扩展:

^2表明第二个父引用记录,能够链式使用。

git checkout HEAD~^2~

 表示上一个父记录,而后再第二个父记录,而后再上一个父记录。

 

若是要在这里创建一个新分支

git branch bugFix HEAD~^2~ 

 

 


 

挑战3 ,git rebase 的再次使用。

HEAD也能够作[目标] 

git rebase [-i] [目标] [要移动的记录] 

 



 

remote repertory

 

简单来讲就是你的仓库在其余机器上的备份。

特色:

  • 备份,恢复丢失数据
  • 远程让代码能够社交化了!其余人能够为你的代码作贡献。 

git clone: 在本地建立一个远程仓库的拷贝 

  


 

 

远程跟踪分支 remote-tracking branch

  

在本地仓库多了一个名为o/master的分支,这种类型的分支叫作 远程跟踪分支。

远程跟踪分支反应了远程仓库(在你上次和它通讯时)的状态。

这有助于理解你本地的工做和公共工做的差异 -- 这是和别人分享工做成果最重要的一步。 

 

特别的属性:在你checkout时,自动进入分离的HEAD状态。 缘由是Git要求,不能在远程跟踪分支上直接写代码,须要先在其余地方写好代码后,更新到远程仓库对应的位置,远程跟踪分支才会更新。

 

git checkout o/master;

git commit;

在有新的commit提交记录后,o/master不会同步更新,会和HEAD分离。

⚠️,Head老是指向当前分支上最后一次的提交记录,但这里是例外。

o/master只有在远程仓库中对应的分支更新后,才会更新。 

 

格式:<remote name>/<branch name> 

简称: o/ 

默认: remote name 是 origin

 


 

Git Fetch 

 

从remote repertory得到数据。

当从远程仓库得到数据时, 远程分支会自动更新,以反应最新的远程仓库。 

 

git fetch会作的事情: 

  • 从远程仓库下载本地仓库缺乏的commit记录
  •  更新远程跟踪分支 如 origin/feature_branch
git fetch 不会作的事情:
  • ⚠️不会更新你的master分支和其余分支,也不会修改你磁盘上的文件。
  • 所以,不是说git fetch后本地仓库就和远程仓库同步了,这是❌的想法。
  • git fetch只是单纯的下载服务。 
//取指定的分支或tags,下载全部须要的commits和文件
git fetch <repository> [<refspec>...] // 取全部remote branch
git fetch --all [<options>]

// 试运行,就是一次彩排,看看会出现什么结果
git fetch --dry-run

 

 

使用git fetch 同步远程仓库:

git fetch origin //会显示咱们下载的branchs
//a1e8fb5..45e66a4 master -> origin/master
//a1e8fb5..9e8ab1c develop -> origin/develop
//* [new branch] some-feature -> origin/some-feature

若是想要查看上游master增长了什么commit,能够运行git log命令,并使用origin/master进行检索:

git log --oneline master..origin/master

 

而后批准这些变化并合并它们到你的本地master分支:

git checkout master git log origin/master

//如今origin/master和master分支指向同一个commit点了,
//而且你和上游upstream 开发同步了。
git merge origin
/master

 


 

Git Pull

 

下载下来后,把远程分支合并到本地的方法: 

  • git cherry-pick o/master
  • git rebase o/master
  • git merge o/master
  • 等等 

 

 git pull 就是直接一步完成2个命令。即git fetch和git merge的方便代码。

 等同于:git fetch 和git cherry-pick o/master

 效果等同于:git fetch, git rebase master o/master, git rebase o/master master。但结构是线性的,由于使用git rebase是线性合并,o/master会指向新C3' 

 


 

模拟团队合做:下载。

  1. git fetch 
  2. 在本地的master分支新增了commit
  3. git merge o/master, 合并远程分支到本地的master。

 


 

Git Push

 

当本地仓库被修改,须要执行git push操做来分享这个修改的代码给team members:

git push <remote> <branch>
git push <remote> -all

 

若是不带任何参数,会使用push.default的设置。他的默认值是正在使用的Git的版本,推送前最好检查一下这个配置 

  


  

偏离的困惑 

 

假如周一你克隆了一个仓库,而后开发某个新功能。到周五时,能够提交到远程仓库了。可是,

这周你的同事写了一堆代码并修改了你还在使用的API。这些变更让你的新开发的功能不可用。

但他已经提交推送到远程仓库了。你的工做变成了基于旧版本的代码,已和远程仓库的最新的代码不匹配了。

 

这时,Git不会容许你push,它会要求你先合并远程最新的代码,而后你才能分享你的工做。

这就是历史偏移 

 

须要:

  1. git fetch
  2. git rebae o/master#这里可能你的新增功能,会失效,
  3. 你须要先修改代码,而后commit。最后:
  4. git push 
还可使用git merge
  1. git fetch#更新本地仓库中的远程分支
  2. git merge o/master#这会产生新的commit记录。C4
  3. git push 

 

--rebase选项: 

git pull --rebase , 等同于用git rebase合并远程分支,而默认是git merge 

 

小结:工做流程: fetch, rebase/merge, push

 

强制push

Git 为了防止你覆盖中央仓库的历史,会拒绝你会致使非快速向前合并的推送请求。

--force 这个标记覆盖了这个行为,让远程仓库的分支符合你的本地分支,删除你上次 pull 以后可能的上游更改。

只有当你意识到你刚刚共享的提交不正确,并用 git commit --amend 或者交互式 rebase 修复以后,你才须要用到强制推送。

⚠️ 可是,你必须绝对肯定在你使用 --force 标记前你的同事们都没有 pull 这些提交。

 


 


 

关于origin和它的周边  --Git 远程仓库高级操做 

 

推送push 主分支

 

大型项目,开发人员会在特性分支上工做,工做完成后只作一次集成。

但有些开发者只在master上push,pull。这样master老是最新的,始终与远程分支保持一致。

 

  • 将特性分支集成到master上。
  • push并更新远程分支 

  


 

为何操做远程分支不喜欢用merge, 见仁见智

 

  •  喜欢干净的提交树,用rebase
  •  喜欢保留提交历史的,用merge

 


 

远程跟踪分支:remote-tracking branches

 

Git 设置了master和o/master的关联。 

  • pull时,commit记录会被先下载到远程分支,如:origin/master上,以后会再合并到master分支。
  • push时,咱们把工做从master推到远程仓库中的master分支,同时更新远程分支origin/master。

 

它们的关联关系,是由"remote tracking" 属性决定的。

master被设定为跟踪o/master。

 

当你克隆远程仓库时,Git会为远程仓库的每一个分支都设定一个远程分支,而后再在本地建立一个和远程仓库

中的分支同样的本地分支。

 

克隆完成后,你会获得本地分支,若是没有就是空白。

 

这也解释了在克隆时会看到下面的输出:

local branch "master" set to track remote branch "o/master" 

本地分支“master”设置跟踪远程分支 "o/master"

 


 

能够指定"remote tracking" 属性属性

 

让任意分支跟踪o/master,而后该分支就会像master分支同样获得隐藏的push目的地和merge的目标。

这意味着你能够在分支XXX上指向git push, 将工做推送到远程仓库的master分支上。

两种设置方法:

 

  • git checkout -b XXX o/master
  • 若是已经有了XXX分支,则使用git branch -u o/master XXX 
-u的意思: -u, --set-upstream-to <upstream>

  

 


 

Git push的参数

 

默认Git经过当前checkout分支的属性来肯定远程仓库和要push的目的地。

 

咱们也能够明确指定push的参数: 

git push <options> <remote-Repository-name> <place> 

例子: 

git push --set-upstream origin master:

把当前分支push, 并设置远程仓库origin为upstream。

 

解释:

这行代码通常用在建立一个远程连接并同步上传数据:

⭠ master ⮀ git remote add origin https://github.com/xxxxxx/yyyyyy.git
//连接远程仓库origin.
⭠ master ⮀ git push -u origin master //Branch master set up to track remote branch master from origin. //本地分支master已经开始追踪远程仓库origin的master分支!

 

<place> 详细解释: 

能够分解为<localbranch-source>:<destination> 

即本地分支source,提交到远程的另外一个分支destination。

<source>能够是任何commit记录位置。 

若是<destination>在远程仓库中并不存在,则会在远程仓库中新建这个分支。

例子: git push origin localbranch:remotebranch

 

强制执行一次non-fast-forward merge

git push origin --force 

⚠️,只有绝对肯定你正在作的才这么用!

 

--all选项:推送全部分支:

git push <remote> --all

 

把标签也推送上去,默认tags是不推送的。

git push <remote> --tags

 

Amended force push

git commit --amend 用于更新提交点,

本质就是把commit原先的改变和更新的内容存入一个新的commit点,扔掉原来的commit点。

 

可是如此,git push 会致使失败,由于Git会发现Amended的commit点和远程的commit点的内容是分离的。

 

此时须要使用--force选项来push一个amended commit!

# make changes to a repo and git add git commit --amend
#此时若是直接git push 会报告错误❌:
# ! [rejected]        master -> master (non-fast-forward)

#须要使用--force
git push
--force origin master

注意⚠️没有同步更新remote-tracking branch!

当本地master分支和origin/master在一块儿时,执行一次修改并commit,再git push, 这时origin/master不能同步更新,须要再次执行一次同步命名。git push ,git fetch均可以.

 

再次提交一次:

#修改一些文件并提交 git commit -am 'd'

 

能够发现origin/master是处于4ebc769,

若是使用

git fetch

 

origin/master会更新到最新commit点。 

 

删除远程分支或者tag

实际是推送一个空的分支替换远程一个分支,至关于删除远程的这个分支。

//查看本地和远程的分支
git branch --all //删除本地分支
git branch -D branch_name //删除远程分支
git push origin origin :branch_name

 

 

 


 

Git remote 

 

如⬆️代码,git remote add origin <url>, 建立了本地和远程仓库origin的连接!

本地./.git/config文件储存了这条记录信息:

[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = https://github.com/chentianwei411/practice.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master config (END)

 

git push -u origin master命令,则添加了[branch "master"]这个记录。

 

全部git remote的操做,都会记录在./.git/config文件中!

//执行git remote remove origin命令: //会断开本地和远程的链接:
//全部对跟踪远程的分支的设置和对远程的配置设置被移除!

//结果: [core] repositoryformatversion
= 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [branch "master"] config (END)

 

再次使用git remote add origin <url>, 便可再链接上远程仓库:见./.git/config文件增长的代码:

[branch "master"] [remote "origin"] url = https://github.com/chentianwei411/practice
        fetch = +refs/heads/*:refs/remotes/origin/*

 

 

git remote get-url --all origin

列出全部的远程链接URLs.

 

git remote show <name>

这条命令会给出详细的关于一个远程链接的配置信息.

先git remote -v命令,查看。而后用git remtoe show <name>:

  • show origin, 
  • show upstream ,
  • shou other_users_repo
* remote origin Fetch URL: https://github.com/chentianwei411/practice
  Push  URL: https://github.com/chentianwei411/practice
 HEAD branch: master Remote branch: master tracked Local ref configured for 'git push': master pushes to master (up to date)

 

 

git remote prune [--dry-run] origin

Deletes all stale remote-tracking branches under <name>.

删除全部过时的远程跟踪分支, 即origin/xxx

⚠️origin/xxx是存在于本地仓库的,用于接收从远程仓库pull下来的数据。而后再merge到xxx分支。

These stale branches have already been removed from the remote repository
referenced by <name>, but are still locally available in "remotes/<name>".

这些过时的分支已经从远程仓库origin移除了,可是在本地仓库仍然能够在remotes/origin中找到:

⭠ master ⮀ git branch --all hotfix * master try  remotes/origin/hotfix remotes/origin/master

 

--dry-run, 列出什么分支会被剪除掉prune!

 git remote prune --dry-run origin Pruning origin URL: https://github.com/chentianwei411/practice
 * [would prune] origin/hotfix

 

git remote prune origin Pruning origin URL: https://github.com/chentianwei411/practice
 * [pruned] origin/hotfix

 

再次使用git branch -a命令查询全部分支,会发现remotes/origin/hotfix分支已经被删除!! 

 

显示你的remotes

git remote

git remote -v

//-v选项,是verbose的意思 // 会列出标记的仓库名字和相关信息,合做的仓库URL.
 origin git@bitbucket.com:origin_user/reponame.git (fetch) origin git@bitbucket.com:origin_user/reponame.git (push) upstream https://bitbucket.com/upstream_user/reponame.git (fetch)
upstream    https://bitbucket.com/upstream_user/reponame.git (push)
other_users_repo    https://bitbucket.com/other_users_repo/reponame (fetch)
other_users_repo    https://bitbucket.com/other_users_repo/reponame (push)

 

 

添加远程仓库

当你添加了一个远程仓库。 你就可使用仓库名字origin做为<url>的简写,在其余git命令上使用了。

这是由于./.git/config中记录了url的信息:

[remote "origin"] url = https://github.com/chentianwei411/practice
        fetch = +refs/heads/*:refs/remotes/origin/*

 

 

 

 


 

 

Git Fetch origin <remotebranch-source>

 

和git push正相反,Git会查找remotebranch的本地远程分支,并下载到本地的远程分支。

这样不会弄坏你的本地同名分支。

 

也能够另外指定远程仓库的分支下载到哪一个本地的分支<destination>,

这样会直接下载到某个分支上(不是远程分支),⚠️开发人员不多这么作,有风险。

 

若是本地没有一个bar分支:

git fetch origin XX:bar的结果是, 会在本地自动建立一个bar分支,用来存储远程仓库的commit信息。

 

若是只有git fetch:

Git会下载远程仓库中全部的提交记录到各个远程分支...

 


 

省去<source> 的特殊用法:

 

删除远程仓库的分支:

git push origin :foo  #推送一个空的source到远程仓库,若是远程仓库有foo分支,这个分支将被删除。

 

git fetch origin :bar  #下载一个远程仓库没有的空分支给本地,本地建立一个bar分支。

 


 

 

Git Pull参数

git pull origin foo 至关于:

git fetch origin foo; git merge o/foo

 

⚠️git pull origin master 会先下载到o/master, 而后merge到当前checkout检出位置。

因此,使用git pull要不熟练,要不就别用。

 

git pull也可使用<source>:<destination>:

如:git pull origin master:foo

  1. 若是本地没有foo, 先在本地建立一个foo分支,
  2. 而后从远程仓库下载master分支中的提交记录并合并到foo分支
  3. 而后再merge到当前的检出分支checkout分支上。

 

 


  

git clean 

 

将未跟踪的文件从你的工做目录working directory中移除。

未跟踪文件是新增到在工做目录但还没有添加到用 git add添加到repo's index。

它只是提供了一条捷径,由于用 git status 查看哪些文件还未跟踪而后手动移除它们也很方便。

和通常的 rm 命令同样,git clean是没法撤消的,因此在删除未跟踪的文件以前想清楚,你是否真的要这么作。

 

git clean 命令常常和 git reset --hard 一块儿使用。

⚠️记住,reset 只影响被跟踪的文件,因此还须要git clean来清理未被跟踪的文件。这个两个命令相结合,你就能够将工做目录回到以前特定提交时的状态。 

# 先测试一下比较好-n ,--dry-run
git clean --dry-run

#
移除当前目录下未被跟踪的文件。-f(强制)标记是必需的。
# 不会删除未跟踪的目录directory和.gitignore中的文件。
git clean -f # 移除未跟踪的文件,但限制在某个路径下 git clean -f <path> # 测试移除未跟踪的目录directory git clean -dn

# 移除未跟踪的目录

git clean -d

 

⚠️请牢记,和 git reset --hard 同样, git clean 是仅有的几个能够永久删除提交的命令之一,因此要当心使用 

栗子:

# 编辑了一些文件 # 新增了一些文件 # 『糟糕』

# 将跟踪的文件回滚回去, 新增文件从head, index, 工做目录中删除了!!修改的文件恢复到以前的代码!!
git reset --hard # 移除未跟踪的文件,先测试-n, -dn
git clean -n git clean -df

 


 

改变旧的或者多个commits: git rebase 

 

rebase改基。从一个分支移动到另外一个分支,即改基。

移动或联合一系列的提交点到一个新的base commit。实际是建立了一系列新的提交点。

 

效果和目的:

⚠️不要用在public commit。

  1. 让你修改你的历史, 而且可交互的改基容许你如此作而不留下杂乱的痕迹。
  2. 让你在修改错误和重新定义你的任务后,仍然保持了一个干净,线性linear的程序历史。

在真实的场景:

  1. 在主分支发现bug.。一个功能分支的功能由此坏掉。
  2. 开发者检查主分支历史git log。 由于clean history,开发者能快速的找出project的历史。
  3. 开发者使用git log仍是不能识别出bug是什么时候插入的introduced。因此他执行了git  bisect
  4. 由于git history很干净, git bisect有一个refined set of commits 来比较。开发者快速的找到了这个插入bug的commit点。

 

例子:

当你在一个功能分支上进行开发时,主分支发现一个bug。

新增一个hotfix分支,用于fix bug。 在搞定bug后, 把bug分支合并到master。

你想要在你的功能分支上使用最新版本的主分支,但你想要保持你的功能分支的历史干净,即好似你一直在最新版的master上开发功能分支。

# 开始新的功能分支
git checkout -b new-feature master # 编辑文件
git commit -a -m "Start developing a feature"


#在 feature 分支开发了一半的时候,咱们意识到项目中有一个安全漏洞:----

# 基于master分支建立一个快速修复分支
git checkout -b hotfix master # 编辑文件
git commit -a -m "Fix security hole"
# 合并回master
git checkout master git merge hotfix git branch -d hotfix #将 hotfix 分支并回以后 master,咱们有了一个分叉的项目历史。----
git checkout new-feature git rebase master #它将 new-feature 分支移到了 master 分支的末端, #而后在master上进行标准的快速向前合并了:
git checkout master git merge new-feature

 

Rebasing的一个经常使用方式是:把upstream的变化集成到你的本地仓库。

 

 移动整个功能分支⬆️

 

git rebase -i <base>

用 -i 标记运行 git rebase 开始交互式 rebase。交互式 rebase 给你在过程当中修改单个提交的机会,而不是盲目地将全部提交都移到新的基上。你能够移除、分割提交,更改提交的顺序。

 

讨论

交互式 rebase 给你了控制项目历史的彻底掌控。它给了开发人员很大的自由,由于他们能够提交一个「混乱」的历史而只需专一于写代码,而后回去恢复干净。

大多数开发者喜欢在并入主代码库以前用交互式 rebase 来完善他们的 feature 分支。他们能够将不重要的提交合在一块儿,删除不须要的,确保全部东西在提交到「正式」的项目历史前都是整齐的。对其余人来讲,这个功能的开发看上去是由一系列精心安排的提交组成的。

 

执行:git rebase -i master 后出现vim的交互界面:

  • 若是删除全部pick,而后:wq保存退出,至关于取消rebase 这条命令。提示:Nothing to do
  • 若是直接:q退出,不作任何修改,至关于执行了git rebase master命令。
    • 提示:Successfully rebased and updated

 

若是只pick 37c8c61,第一行。则提示成功:

Successfully rebased and updated refs/heads/b2.

 
而后就能够快速前进的合并了:
git checkout master git merger b2

 

⚠️另外2个pick就被分支b2扔掉了!!

若是知道这2个commit的id,就能够进入它们:

git chekcout xxxx

获得提示信息You are in 'detached HEAD' state. 你处于分离的HEAD!

能够把这2个提交点合并到b2, 再把b2合并到master。

 

Rebasing的其余几个有趣的命令:

  • edit
  • reword -能够重写提交信息message
  • squash -把当前的commit合并到上一个commit, 并提示你重写提交message
  • fixup    -和squash同样,不会重写提交message.

 

edit 9a29b81 

如可使用命令edit, 代替pick,当:wq保存退出vim后,提示

⭠ b3 ⮀ git rebase -i master Stopped at bb28411... change yangcheng You can amend the commit now, with 
git commit
--amend
Once you are satisfied with your changes, run
git rebase
--continue

➦ bb28411 ⮀

 

而后你能够在这个提交点上作修改,而后

➦ bb28411± ⮀ git add . ➦ bb28411± ⮀ git commit --amend [detached HEAD 36f12d6] change yangcheng Date: Sun Nov 25 11:45:44 2018 +0800
 1 file changed, 1 insertion(+), 1 deletion(-) ➦ 36f12d6 ⮀ git rebase --continue Successfully rebased and updated refs/heads/b3. ⭠ b3 ⮀

 

squash 9a29b81

合并后放入新增的一个commit中。 这是rebase的主要功能!!

缘由:

大量的细小的改动,每一个改动都是一个提交。致使仓库的history看起来很乱。

许多这样的commit并无实际地给你的仓库history增长任何价值

它们弄乱了blame(新旧版本的对比), make bisects take longer and make the history hard to navigate.

⭠ b4 ⮀ git rebase -i master

#此时进入vim编辑器。重写提交message,而后:wq
[detached HEAD fac0e93] change content
and guangzhou Date: Sun Nov 25 12:42:45 2018 +0800 2 files changed, 3 insertions(+), 1 deletion(-) Successfully rebased and updated refs/heads/b4.

 

而后:

git checkout master git merge b4
git branch -d b4

 

还有另外一种使用squash的方式:

git merge --squash <commit> 能够把一个分支合并为一个commit, 而后执行

git commit -m 'squash ...' 

 

最后删除分支b3:  git branch -D b3


 

安全网:git reflog  

Git保持对分支的tip的追踪!这种机制叫作reflog(reference logs), 引用日志。

Git 用引用日志这种机制来记录分支顶端的更新和其余commit引用。

它容许你回到那些不被任何分支或标签引用的commits。在重写历史后,reflog包含了分支旧状态的信息,有须要的话你能够回到这个状态。

每次当你的分支tip被任何缘由所更新(包括切换分支,pulling in new changes, 重写历史或者仅仅是增长新的commits), 一个新的entry将被增长到reflog。

reflog提供了一张安全网,全部的分支顶端的变化都会被记录。

另外, reflog提供了到期日。默认设置expiration time是90天。

 

用法

git reflog # 是git reflog show HEAD的简写

显示: 

fac0e93 HEAD@{0}: merge b4: Fast-forward 6f70a12 HEAD@{1}: checkout: moving from b4 to master fac0e93 HEAD@{2}: rebase -i (finish): returning to refs/heads/b4 fac0e93 HEAD@{3}: rebase -i (squash): change content and guangzhou 0481ff2 HEAD@{4}: rebase -i (pick): change content 6f70a12 HEAD@{5}: rebase -i (start): checkout master 3bc3338 HEAD@{6}: checkout: moving from master to b4 。。。
#fac0e96,第一行是最新的一次relog。

 

还能够用:

git reflog --relative-date

fac0e93 HEAD@{3 hours ago}: merge b4: Fast-forward
6f70a12 HEAD@{3 hours ago}: checkout: moving from b4 to master
fac0e93 HEAD@{4 hours ago}: rebase -i (finish): returning to refs/heads/b4
...

#显示相对如今,每条记录发生的时间!

 

默认,git reflog会输出HEAD ref。但也能够显示其余ref。

如其余分支,tags, remotes, Git stash均可以被引用。

引用中的语法格式:name@{qualifier}

得到所有的reflog:

git reflog show --all

 

查看具体某一个分支的引用:

⭠ b5 ⮀ git reflog show b5 //显示:
905b28c b5@{0}: commit: change bj fac0e93 b5@{1}: branch: Created from HEAD (END)

 

若是使用过git stash命令储存了缓存区的文件, 而且还未取出,则能够用git reflog命令查看记录:

git reflog stash
//显示
bd1f5f7 stash@{0}: WIP on b5: 905b28c change bj

 

 

另外, 可使用git diff: Show changes between commits, commit and working tree

//git diff stash@{0} otherbranch@{0}
⭠ b5 ⮀ git diff stash@{0} b5@{0}
//显示 diff --git a/beijing.txt b/beijing.txt index 7719339..6815264 100644 --- a/beijing.txt +++ b/beijing.txt @@ -1,3 +1,4 @@ +123 hahaha hello hello this is a beautiful city! (END)

 

通常用不到,使用图形编辑器sourcetree,就可直观的看一个commit的变化。

但用代码,能够有更丰富的细节设置,如加上一个到期的时间:

git diff master@{0} master@{1.day.ago}

 

具体可见git diff --help

 

恢复丢失的commits

Git 从不真地丢失任何东西,继续执行了历史重写操做,如rebasing, commit amending。

 

git log的搜索功能很强大,有丰富的设置能够查看各类状况。

 

例如:

分支b5有4个commit,master有1个commit超过b5

⭠ b5 ⮀ git rebase -i master //进入vi编辑器。把第4行的pick改为squash, :wq。 //terminal:
[detached HEAD 55ce6f9] change one Date: Sun Nov 25 17:06:31 2018 +0800
 2 files changed, 3 deletions(-) Successfully rebased and updated refs/heads/b5. 

 结果附加到master上的commit只有3个,最后一个是合并的commit

git log --pretty=oneline //也只能看到新增的3行log
55ce6f9c6f60d80f9b042f5f8a99556333d5854a change one 23bd2a5ead2b5a19d6bcc8667fbd23c636135264 change sth c4e32fb82669aa16ac60e2b9f8a1bc488a366be5 change bj

 

彷佛b5分支的最后2个commit,因为squash,致使没法找到了!其实否则:

使用git reflog命令,便可看到最新的ref日志⬇️:

55ce6f9 HEAD@{0}: rebase -i (finish): returning to refs/heads/b5 55ce6f9 HEAD@{1}: rebase -i (squash): change one 07e7fbf HEAD@{2}: rebase -i (pick): 1 23bd2a5 HEAD@{3}: rebase -i (pick): change sth c4e32fb HEAD@{4}: rebase -i (pick): change bj eb80dcf HEAD@{5}: rebase -i (start): checkout master 1c226ea HEAD@{6}: checkout: moving from master to b5

能够看到从start到finish的所有细节:一部了然!! 

  • 4行绿色是4个commit点
  • 第2行操做的方式是squash

若是想要恢复到执行git rebase以前,可使用:

git reset HEAD@{6}



 

 

保存改变的5个命令:

  • git add

  • git commit

  • git diff (比较commit的不一样,能够用可视化工具:sourcetree, 或者上远程仓库,在网页上看。)

  • git stash(上面已经介绍,把staged和未staged的文件储存起来)

  • .gitignore :这里就介绍它。

 

.gitignore 

Git从copy的行为上分类: 把文件分红3种类别:

  1. tracked -一个文件以前被staged or commited
  2. untracked -一个从未被staged or commited的文件,通常是新建的文件。
  3. ignored -不加入tracked的文件。

包括:

  • 独立缓存/packages
  • build output directories:如 /bin, /out, /target
  • compiled code:  .pyc, .class文件
  • 在运行时产生的文件:  .log, .lock, .tmp
  • 隐藏的系统文件:  .DS_store, Thumbs.db
  • 我的的IDE配置文件

通常能够在~/.gitignore文件内查看仓库中被忽略的文件:

当你有新的文件须要被忽略,.gitignore文件必须手动编辑和提交。

 

Global Git ignore rules

你须要本身创建.gitignore, 并设置core.excludesFile属性。

$ touch ~/.gitignore $ git config --global core.excludesFile ~/.gitignore

 

 

Shared .gitignore files in your repository

一般,Git ignore rules被定义在仓库根目录的.gitignore文件中。

但也能够在你的仓库中的不一样的目录中定义多个.gitignore文件。

然而最简单的方法仍是在根目录建立.gitignor文件,由于它自己也是被版本控制的,当你push,就能够和团队共享。

 

Personal Git igonore rules

你也能够定义我的的ignore模式,这在特殊的.git/info/exclude文件中。

它不会被版本控制!因此你能够作一些私事!

 

如何忽略一个以前commit过的文件?

从仓库删除这个文件,而后增长一个.gitignore rule.

使用 --cached选项和git rm

git-rm - Remove files from the working tree and from the index. --cached: unstage and remove paths only from the index. 但会保留在working directory!

例子:

$ echo debug.log >> .gitignore $ git rm --cached debug.log //提示:rm 'debug.log'  $ git commit -m "Start ignoring debug.log"

[master 6d51c73] Start ignoring debug.log
1 file changed, 1 deletion(-)
delete mode 100644 debug.log


.gitignore自身是被追踪的!,须要git add .gitignore

 

 

Committing an ignored file

能够强制一个被忽略的文件被提交到仓库,使用-f选项便可:

$ cat .gitignore *.log $ git add -f debug.log $ git commit -m "Force adding debug.log"

 

固然,这不是一个明显的,可让团队成员了解的方法:改成:

$ echo !debug.log >> .gitignore $ cat .gitignore  *.log  !debug.log $ git add debug.log $ git commit -m "Adding debug.log"

 

*.log表示全部带.log后缀的文件都会被忽略,可是!dubug.log表示这个文件不会被忽略!

 

Stashing an ignored file

使用git stash回你历史的存储本地的变化,并在以后用git stash pop取回。

可是默认git stash忽略.gitignore中的文件。

使用--all选项,能够stash 忽略的和未跟踪的文件。

具体git stash 说明文档见:https://www.atlassian.com/git/tutorials/git-stash/#stashing-untracked-or-ignored

 

Debugging .gitignore files

若是你有复杂的.gitignore模式或者有多个.gitignore文件,那么想要知道一个文件为什么被忽略,就比较麻烦。

你可使用git check-ignore -v <file-name>来看什么模式让这个文件被忽略

 


 

 

git rm

用于把一个文件从a Git repository中移除(working directory和index, 或者只从index中移除)。

某种程度上能够认为是git add命令的相反命令。

 

git rm命令能够用于移除单独的文件或一组文件集合。

它的主要功能是从Git index中移除跟踪的文件。

另外它也能同时从staging index和 working directory移除文件。

注意git rm 不会移除branches。

 

选项--cached: 

这个选项的用途是取消文件的tracking,但保留这个文件在working directory!

案例见上.gitignore

 

如何undo git rm?

git rm命令须要执行git commit来生效。

git rm将更新staging index 和 working directory,

⚠️这些变化不会立刻产生效果,只有当新的commit被建立,这些变化才会被添加到commit history中。

这就意味着git rm能够恢复。(符合git 做为时光机的原则)执行git reset HEAD

 

⚠️git rm只能用在当前current branch的文件!

 

为何使用git rm来代替rm

当一个被跟踪的文件被rm命令执行,Git仓库会识别这个标准的壳命令rm。

Git仓库会更新working directory来反应这个移除。但它不会更新staging index。

因此须要额外的git add命令,把这个移除的改变添加到staging index。

git rm 是一个方便的shorcut。它将同时更新working directory and the staging index 。

例子:取消文件shanghai.txt的跟踪,并删除这个文件。

⭠ master ⮀ git rm shanghai.txt 提示:rm 'shanghai.txt'
⭠ master± ⮀ git status 提示: On branch master Your branch is ahead of 'origin/master' by 4 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: shanghai.txt   #帮你使用了git add命令。

 

⚠️sourcetree,可使用discard,取消一个文件的修改或删除。就是恢复操做!

等同命令:

git checkout <file>
git checkout -- <file> #加上-- 防止文件名和branch名字重复致使❌。

 

 

Unstage a file:

git reset HEAD <file>  
git rm --cached <file> #删除staging保留working direotry中的文件。

 

 

小节:

git rm --cache <file>用于再也不对某个文件进行tracking。以前commi过的文件,再也不跟踪!

git clean -df 删除未跟踪的文件和目录

git reset --hard 回滚跟踪的文件,即恢复文件到未修改的状态并再也不跟踪,新增的文件则从工做目录删除。 

 



 

 

git fetch 命令是如何与remote branches 合做的?(深刻分析)

Behind the scenes,在幕后, ./.git/objects目录中,Git存储了全部的commits, 包括local和remote。

~/practice/.git/objects ⮀ ⭠ master ⮀ ls
00   0d   1b   24   38   40   51   60   70 7e 8b 9b a8 bb c7 d4 e7 fd 02   11   1c   27   3b   43   55   65   76   81   90   9d   ab   bd   c8   de   eb   info
04   15   1f   2d   3c   49   59   68   77   84   92   a1   ad   be   ce   e0   f0   pack

 

经过使用branch refs(就是commit点的🆔), GIT让分支commits清楚的分开。

远程分支的Refs,  储存在./.git/refs/remotes/

//👇两条命令均可以查看
git branch -r git branch --all

 

本地分支的Refs,储存在./.git/refs/heads/

~/practice/.git/refs/heads ⮀ ⭠ master ⮀ ls hotfix master try

 less try能够看到:

ad64e76234f1dee8ea19618d4f5964edbc20bd36 try (END)

 


 

git log

针对commit点的查询和搜索功能:

//在屏幕上显示几行
git log -n <limit>

//单行显示,用于全局的概览, 内容包括id和messages
git log --oneline 
// 显示每一个commit的文件改变的概览 git log
--stat //使用-p选项,看每一个提交点的patch。比--stat更详细的每一个commit的区别 git log -p

  

经过对message进行匹配来搜索,<pattern>能够是字符串和正则表达式:

git log --grep="<pattern>"

  

看一个特定文件的提交历史:

git log <file>

  

显示一个相似sourcetree的结构:

git log --graph --decorate --oneline

 

⚠️,选项能够一块儿使用。

 

 

git blame

  • The high-level function of git blame is the display of author metadata attached to specific committed lines in a file. This is used to explore the history of specific code and answer questions about what, how, and why the code was added to a repository.

用于比较commit点的历史代码。信息还包括添加代码的做者名字,解释。

合做者能够查看这些历史代码。

通常使用UGI网页,如在线git上查看这些历史代码。

例子:example

git clone https://kevzettler@bitbucket.org/kevzettler/git-blame-example.git 
cd git-blame-example

//使用git log查看全部commit信息。
git log

 

 git blame只用于单独的文件的历史commit点的比较。须要提供file-path来输出信息。

//输出帮助信息
git blame //输出文件的commit历史: 即每一个commit的代码的增减。
git blame README.md
相关文章
相关标签/搜索