Git

Git

1.基础

①版本控制

  • 在使用word写论文的时候,为了重复率不得不删除某段,查重完成之后发现重复率远远低于红线,于是又想要找回原来的那段。但是论文的版本过多,很难找到是哪个版本的。于是,想拥有一个软件,可以记录各个版本。

  • 集中式版本控制,需要一个中央服务器,只有中央服务器上拥有完整的版本库。每次干活从中央服务器中下载项目,改完了在上传上去。缺点:需要联网,如果项目过大,网速又不给力,将浪费时间。此外,中央服务器出现故障,将造成严重后果。

  • 分布式版本控制,每个人的电脑都是完整的版本库,无需联网。当然,在多人协作时,也需要一个中央服务器,双方对同一个项目同一个版本做完修改后,可以通过该中央服务器传递各自的修改内容。

②版本库

版本库(repository)又名仓库,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

  • 创建一个空目录。

  • 通过git init命令把这个目录变成Git可以管理的仓库。

    $ git init
    Initialized empty Git repository in /Users/michael/learngit/.git/
    
  • 将文件放入仓库中:$ git add readme.txt

  • 提交:git commit -m "wrote a readme file",-m后面输入的是本次提交的说明。

2.远程仓库

①SSH

  • SSH(Secure Shell),与https中的SSL类似,SSH也是一种协议,采用的非对称加密。
  • 指定用户名:$ git config --global user.name "fangpeng"
  • 指定邮箱:$ git config --global user.email "279948813@qq.com"
  • Git客户端生成密钥对命令:ssh-keygen -t rsa -C "xxx@xxx.com"。会在主目录生成两个文件,id_rsa.pub是公钥,id_rsa是私钥。终端输入 open ~/.ssh打开主目录。
  • 将公钥配置到gitlab服务器上。

公钥加密必须由私钥解密,私钥加密必须由公钥解密。

②操作

同步:

  • 在gitlab、github、coding等远程仓库管理软件上创建好一个远程仓库。
  • 本地仓库执行命令:git remote add origin git@gitlab.com:fangpeng8/learngit.git,其中@gitlab.com:fangpeng8/learngit.git可以成功定位到远程仓库。
  • 关联成功后,就可以push/pull了。命令$ git push -u origin master就是把本地库推送到远程。第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

SSH警告:

当第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告:

The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?

这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。

Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:

Warning: Permanently added 'github.com' (RSA) to the list of known hosts.

删除:

如果添加的时候地址写错了,或者就是想删除远程库,可以用git remote rm <name>命令。使用前,建议先用git remote -v查看远程库信息:

$ git remote -v
origin  git@github.com:michaelliao/learn-git.git (fetch)
origin  git@github.com:michaelliao/learn-git.git (push)

然后,根据名字删除,比如删除origin

$ git remote rm origin

此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到GitHub,在后台页面找到删除按钮再删除。

③克隆

假设从零开发,那么最好的方式是先创建远程库,然后,从远程库克隆。

用命令git clone克隆一个本地库:

$ git clone git@github.com:michaelliao/gitskills.git

3.版本管理

①版本状态

  • 命令git status查看当前版本修改状态:

    $ git status
    On branch master
    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.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    

    可以看到readme.txt被修改了,还没有被提交。

  • 命令git diff查看某文件的差异

    $ git diff readme.txt 
    diff --git a/readme.txt b/readme.txt
    index 46d49bf..9247db6 100644
    --- a/readme.txt
    +++ b/readme.txt
    @@ -1,2 +1,2 @@
    -Git is a version control system.
    +Git is a distributed version control system.
     Git is free software.
    

②版本回退

  • 强制回到某个版本,目前所做的修改都丢弃:git reset --hard 版本号 文件名

  • 回到某个版本,目前所做的修改不丢弃:git reset --soft 版本号 文件名

  • 不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。

  • Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失。

  • 命令git log查看版本的提交记录

    $ git log
    commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
    Author: Michael Liao <askxuefeng@gmail.com>
    Date:   Fri May 18 21:06:15 2018 +0800
    
        append GPL
    
    commit e475afc93c209a690c39c13a46716e8fa000c366
    Author: Michael Liao <askxuefeng@gmail.com>
    Date:   Fri May 18 21:03:36 2018 +0800
    
        add distributed
    
    commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
    Author: Michael Liao <askxuefeng@gmail.com>
    Date:   Fri May 18 20:59:18 2018 +0800
    
        wrote a readme file
    

    如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:

    $ git log --pretty=oneline
    1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
    e475afc93c209a690c39c13a46716e8fa000c366 add distributed
    eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file
    
  • 在Git中,用HEAD表示当前版本,也就是最新的提交1094adb...,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

  • 使用命令$ git reset --hard HEAD^,将当前版本append GPL回退到上一个版本add distributed

  • 版本回退后,又后悔了,返现上个版本的一些修改还是有用的,想要找回来。使用git log发现最新的那个版本append GPL已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了。

    办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个append GPLcommit id1094adb...,于是就可以指定回到未来的某个版本:

    $ git reset --hard 1094a
    HEAD is now at 83b0afe append GPL
    

    版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

  • 如果上面的命令窗口关闭了,无法找到上个版本的版本号,还是有后悔药的。Git提供了一个命令git reflog用来记录你的每一次命令:

    $ git reflog
    e475afc HEAD@{1}: reset: moving to HEAD^
    1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
    e475afc HEAD@{3}: commit: add distributed
    eaadf4e HEAD@{4}: commit (initial): wrote a readme file
    

    可以通过这个命令找到版本号。

③暂存区

工作区、版本库、暂存区三者的关系

  • 工作区:电脑上能看到的目录。$ git status查看的就是工作区的状态

  • 版本库(repository):存放在隐藏目录.git文件中。包含了暂存区、第一个分支(master)和指向master的一+ 个指针HEAD。

  • 暂存区(stage):暂时存放文件的地方。把文件往Git版本库里添加的时候,是分两步执行的,用git add把文件添加进去,实际上就是把文件修改添加到暂存区;用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

  • 如果一个文件修改了,没有git add,而是直接git commit,那么这个文件的修改不会被提交,因为其没有被放到暂存区。

总结

  • 将单个文件从工作区加到暂存区:git add + 文件名

  • 将多个文件从工作区加到暂存区:git add + 文件名1 文件名2

  • 将多有文件从工作区加到暂存取:git add all

④撤销修改

  • git add到stage:$ git checkout -- readme.txt。把readme.txt文件在工作区的修改全部撤销。这里有两种情况:

    一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

    一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

    总之,就是让这个文件回到最近一次git commitgit add时的状态。

    注意:该命令是--

  • git add到stage,未commit:$ git reset HEAD readme.txt。可以把暂存区的修改撤销掉(unstage),重新放回工作区。git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

  • 已commit,未push:git reset -head HEAD^回退版本。

总结:

  • 文件修改后未放置到暂存区,从版本库中获取:git checkout -- 文件名
  • 文件添加至暂存区,又作出修改,撤销至暂存区状态:git checkout -- 文件名

这里的checkout其实是用版本库里的版本替换工作区的版本,无论修改还是删除,都可以进行还原。

⑤删除文件

  • 一般情况下,通常直接在文件管理器中把没用的文件删了

  • 这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了:

    $ git status
    On branch master
    Changes not staged for commit:
      (use "git add/rm <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
    	deleted:    test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
  • 确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit

    $ git rm test.txt
    rm 'test.txt'
    
    $ git commit -m "remove test.txt"
    [master d46f35e] remove test.txt
     1 file changed, 1 deletion(-)
     delete mode 100644 test.txt
    
  • 如果是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本。

    $ git checkout -- test.txt
    

    git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

4.分支管理

https://blog.csdn.net/qq_37772867/article/details/104924533

①创建与合并

概念:

在版本回退里,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

命令:

  • 创建dev分支,然后切换到dev分支:$ git checkout -b dev,加上-b相当于以下两条命令:

    $ git branch dev
    $ git checkout dev
    Switched to branch 'dev'
    
  • 查看当前分支:

    $ git branch
    * dev
      master
    
  • 切换分支,dev分支的工作完成,我们就可以切换回master分支:

    $ git checkout master
    Switched to branch 'master'
    

切换回master分支后,再查看文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变

  • dev分支的工作成果合并到master分支上:

    $ git merge dev
    Updating d46f35e..b17d20e
    Fast-forward
     readme.txt | 1 +
     1 file changed, 1 insertion(+)
    

    git merge命令用于合并指定分支到当前分支。

  • 合并完成后,就可以放心地删除dev分支:

    $ git branch
    * master
    
  • 注意到切换分支使用git checkout <branch>,前面的撤销修改则是git checkout -- <file>,同一个命令,有两种作用,确实有点令人迷惑。

    实际上,切换分支这个动作,用switch更科学。因此,最新版本的Git提供了新的git switch命令来切换分支:

    创建并切换到新的dev分支,可以使用:

    $ git switch -c dev
    

    直接切换到已有的master分支,可以使用:

    $ git switch master
    

②冲突

对于一个文件,两个分支都有了新的提交,如:master分支和feature1分支各自都分别有新的提交

Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突:

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

此时,可以在master分支再次对readme.txt文件进行修改再提交:

可以用带参数的git log也可以看到分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit
*   cf810e4 (HEAD -> master) conflict fixed
|\  
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/   
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file

③分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

  • master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活。
  • 干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
  • 我们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

④多人协作

当从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin

要查看远程库的信息,用git remote

$ git remote
origin

或者,用git remote -v显示更详细的信息:

$ git remote -v
origin  git@github.com:michaelliao/learngit.git (fetch)
origin  git@github.com:michaelliao/learngit.git (push)

上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

$ git push origin master

如果要推送其他分支,比如dev,就改成:

$ git push origin dev

但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要时刻与远程同步;
  • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
  • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
  • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

抓取分支

多人协作时,大家都会往masterdev分支上推送各自的修改。

现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:

$ git clone git@github.com:michaelliao/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 40, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 40 (delta 14), reused 40 (delta 14), pack-reused 0
Receiving objects: 100% (40/40), done.
Resolving deltas: 100% (14/14), done.

当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。不信可以用git branch命令看看:

$ git branch
* master

现在,你的小伙伴要在dev分支上开发,就必须创建远程origindev分支到本地,于是他用这个命令创建本地dev分支:

$ git checkout -b dev origin/dev

现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:

$ git add env.txt

$ git commit -m "add env"
[dev 7a5e5dd] add env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 308 bytes | 308.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   f52c633..7a5e5dd  dev -> dev

你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:

$ cat env.txt
env

$ git add env.txt

$ git commit -m "add new env"
[dev 7bd91f1] add new env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ git push origin dev
To github.com:michaelliao/learngit.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:

$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> dev

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置devorigin/dev的链接:

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再pull:

$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.

这回git pull成功,但是合并有冲突,需要手动解决。解决后,提交,再push:

$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict

$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   7a5e5dd..57c53ab  dev -> dev

⑤Rebase

多人在同一个分支上协作时,很容易出现冲突。即使没有冲突,后push的童鞋不得不先pull,在本地合并,然后才能push成功。

每次合并再push后,分支变成了这样:

$ git log --graph --pretty=oneline --abbrev-commit
* d1be385 (HEAD -> master, origin/master) init hello
*   e5e69f1 Merge branch 'dev'
|\  
| *   57c53ab (origin/dev, dev) fix env conflict
| |\  
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
| |/  
* |   12a631b merged bug fix 101
|\ \  
| * | 4c805e2 fix bug 101
|/ /  
* |   e1e9c68 merge with no-ff
|\ \  
| |/  
| * f52c633 add merge
|/  
*   cf810e4 conflict fixed

总之看上去很乱,为什么Git的提交历史不能是一条干净的直线?

其实是可以做到的!

Git有一种称为rebase的操作,有人把它翻译成“变基”。

先不要随意展开想象。我们还是从实际问题出发,看看怎么把分叉的提交变成直线。

在和远程分支同步后,我们对hello.py这个文件做了两次提交。用git log命令看看:

$ git log --graph --pretty=oneline --abbrev-commit
* 582d922 (HEAD -> master) add author
* 8875536 add comment
* d1be385 (origin/master) init hello
*   e5e69f1 Merge branch 'dev'
|\  
| *   57c53ab (origin/dev, dev) fix env conflict
| |\  
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
...

注意到Git用(HEAD -> master)(origin/master)标识出当前分支的HEAD和远程origin的位置分别是582d922 add authord1be385 init hello,本地分支比远程分支快两个提交。

现在我们尝试推送本地分支:

$ git push origin master
To github.com:michaelliao/learngit.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

很不幸,失败了,这说明有人先于我们推送了远程分支。按照经验,先pull一下:

$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:michaelliao/learngit
   d1be385..f005ed4  master     -> origin/master
 * [new tag]         v1.0       -> v1.0
Auto-merging hello.py
Merge made by the 'recursive' strategy.
 hello.py | 1 +
 1 file changed, 1 insertion(+)

再用git status看看状态:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

加上刚才合并的提交,现在我们本地分支比远程分支超前3个提交。

git log看看:

$ git log --graph --pretty=oneline --abbrev-commit
*   e0ea545 (HEAD -> master) Merge branch 'master' of github.com:michaelliao/learngit
|\  
| * f005ed4 (origin/master) set exit=1
* | 582d922 add author
* | 8875536 add comment
|/  
* d1be385 init hello
...

现在事情有点不对头,提交历史分叉了。如果现在把本地分支push到远程,有没有问题?

有!

什么问题?

不好看!

有没有解决方法?

有!

这个时候,rebase就派上了用场。我们输入命令git rebase试试:

$ git rebase
First, rewinding head to replay your work on top of it...
Applying: add comment
Using index info to reconstruct a base tree...
M	hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
Applying: add author
Using index info to reconstruct a base tree...
M	hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py

输出了一大堆操作,到底是啥效果?再用git log看看:

$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master) add author
* 3611cfe add comment
* f005ed4 (origin/master) set exit=1
* d1be385 init hello
...

原本分叉的提交现在变成一条直线了!这种神奇的操作是怎么实现的?其实原理非常简单。我们注意观察,发现Git把我们本地的提交“挪动”了位置,放到了f005ed4 (origin/master) set exit=1之后,这样,整个提交历史就成了一条直线。rebase操作前后,最终的提交内容是一致的,但是,我们本地的commit修改内容已经变化了,它们的修改不再基于d1be385 init hello,而是基于f005ed4 (origin/master) set exit=1,但最后的提交7e61ed4内容是一致的。

这就是rebase操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了。

最后,通过push操作把本地分支推送到远程:

Mac:~/learngit michael$ git push origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 576 bytes | 576.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To github.com:michaelliao/learngit.git
   f005ed4..7e61ed4  master -> master

再用git log看看效果:

$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master, origin/master) add author
* 3611cfe add comment
* f005ed4 set exit=1
* d1be385 init hello
...

远程分支的提交历史也是一条直线。

5.IDEA

①开始

  • 在coding、gitlab或github上建立一个远程仓库
  • 在idea上创建一个项目,并设置远程仓库地址为远程仓库上的地址,一般为ssh地址
  • 打开Terminal终端,输入命令git init将该项目文件夹设置为git本地仓库,此时本地仓库有个默认的分支master
  • 使用IDEA完成一次commit和push,则远程仓库上会同步一个master分支与之对应
  • 同理,如果想在远程仓库上有一个dev分支,现在本地创建一个dev分支,之后commit和push,则远程仓库上会同步一个dev分支与之对应

②Git-Amend

有时你提交过代码之后,发现一个地方改错了,你下次提交时不想保留上一次的记录

或者你上一次的commit message的描述有误

这时候你可以使用接下来的这个命令:git commit --amend

③GitIgnore

概念

在工程中,并不是所有文件都需要保存到版本库中的,例如“target”目录及目录下的文件就可以忽略。在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件或目录。

优先级

在 .gitingore 文件中,每一行指定一个忽略规则,Git 检查忽略规则的时候有多个来源,它的优先级如下(由高到低):

  • 从命令行中读取可用的忽略规则
  • 当前目录定义的规则
  • 父级目录定义的规则,依次递推 $GIT_DIR/info/exclude 文件中定义的规则
  • core.excludesfile中定义的全局规则

语法

  • 空格不匹配任意文件,可作为分隔符,可用反斜杠转义
  • 开头的文件标识注释,可以使用反斜杠进行转义
  • ! 开头的模式标识否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用 ! 也不会再次被包含。可以使用反斜杠进行转义
  • / 结束的模式只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件
  • / 开始的模式匹配项目跟目录
  • 如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录
  • ** 匹配多级目录,可在开始,中间,结束
  • ? 通用匹配单个字符
  • *通用匹配零个或多个字符
  • [] 通用匹配单个字符列表

常用匹配忽略

bin/: 忽略当前路径下的bin文件夹,该文件夹下的所有内容都会被忽略,不忽略 bin 文件
/bin: 忽略根目录下的bin文件
/*.c: 忽略 cat.c,不忽略 build/cat.c
debug/*.obj: 忽略 debug/io.obj,不忽略 debug/common/io.obj 和 tools/debug/io.obj
**/foo: 忽略/foo, a/foo, a/b/foo等
a/**/b: 忽略a/b, a/x/b, a/x/y/b等
!/bin/run.sh: 不忽略 bin 目录下的 run.sh 文件
*.log: 忽略所有 .log 文件
config.php: 忽略当前路径的 config.php 文件

我的忽略

HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
/logs/

6.命令大全

①全局配置

  • 让该文件夹变成本地git仓库:git init

  • 查看所有的配置:git config --list

  • 修改用户名:git config --global user.name "fangpeng"

  • 查看用户名:git config user.name

  • 修改邮箱: git config --global user.email "279948813@qq.com"

  • 查看邮箱:git config user.email

  • 查看远程库的url:git remote -v

  • 让Git显示颜色:git config --global color.ui true

  • 显示隐藏文件,如.git版本库被隐藏:ls -ah

②查看状态

  • 查看工作区文件的状态:git status-s可简短输出

    无文件改变

    On branch dev
    nothing to commit, working tree clean
    

    有文件改变

    On branch dev
    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:   src/main/java/com/fang/peng/learngit/LearnGitApplication.java
    
  • 查看工作区某修改后文件的差异:git diff 文件名

  • 查看版本库中各个版本:git log

    commit f51f25d81d1b7e0afc012be306f56bbe7cea6cca (HEAD -> dev, origin/dev)
    Author: fangpeng8 <fangpeng8@jd.com>
    Date:   Wed Sep 22 20:23:48 2021 +0800
    
        <E4><BF><AE><E6><94><B9>
    
    commit 719861948600666686af740d1683a56252cfeeff (origin/master)
    Author: fangpeng8 <fangpeng8@jd.com>
    Date:   Wed Sep 22 19:46:36 2021 +0800
    
        feat: <E5><88><9D><E5><A7><8B><E5><8C><96>
    

    省略版:$ git log --pretty=oneline

  • 查看所有分支:git branch

  • 查看所有分支:git branch -a

    * dev
      master
      show
      remotes/origin/dev
      remotes/origin/master
    

    其中,带*号的为当前分支

  • 查看历史命令:git reflog

    f51f25d (HEAD -> dev, origin/dev, show) HEAD@{0}: commit: <E4><BF><AE><E6><94><B9>
    7198619 (origin/master) HEAD@{1}: checkout: moving from master to dev
    69ec1a0 (master) HEAD@{2}: checkout: moving from dev to master
    7198619 (origin/master) HEAD@{3}: checkout: moving from master to dev
    69ec1a0 (master) HEAD@{4}: commit: <E5><BB><BA><E7><AB><8B>dev<E5><88><86><E6><94><AF>
    7198619 (origin/master) HEAD@{5}: commit (initial): feat: <E5><88><9D><E5><A7><8B><E5><8C><96>
    
    

    可查看对哪些版本的哪些操作

  • 将文件添加到stage中:$ git add readme.txt

  • 将stage中的文件提交到当前分支:$ git commit -m "wrote a readme file"-m后面输入的是本次提交的说明。

  • 通过vi修改文件:vi有两种模式,一种是命令模式,一种是编辑模式。在命令模式下,输入i或者insert键进入编辑模式。在编辑模式下,可以通过ESC进入命令模式。命令模式下的常见指令:输入:wq(注意冒号)ZZ保存修改并退出vi。放弃所有修改:输入:q!+回车。

  • 查看在你上次提交之后是否有对文件进行再次修改:$ git status -s 参数可获得简短输出。

  • 查看工作区和版本库中最新版本的区别:git diff HEAD -- readme.txt

  • 回退上个版本:$ git reset --hard HEAD^$ git reset --hard 1094a。1094a为目标版本号。

  • 用版本库中的文件替换工作区的文件:$ git checkout -- test.txt,无论工作区是修改还是删除,都可以“一键还原”。

③版本问题

在Git中,HEAD表示当前版本,HEAD表示上个版本,HEAD^表示上上个版本,往上一百个版本可以用HEAD~100表示。

回退版本

  • 回退到上个版本:$ git reset --hard HEAD^HEAD^可以使用版本号代替。

  • 回退之后,最新的那个版本有消失了,还可以$ git reset --hard 1094a指定回到未来的版本,1094a是版本号,可以不用写全。

  • 如果忘记了未来版本的版本号,可以通过$ git reflog查看历史命令,从而获取版本号。

撤销修改

  • 场景1:改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。将该文件恢复到暂存区或者版本库中的版本。

  • 场景2:不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。

  • 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

  • 场景4:推送到版本库中了,此时可以再次修改会原样,再推送。

删除文件

  • 删除工作区的文件: rm test.txt。如果该文件已经被commit了,将导致工作区和版本库不一致。此时有两种选择。将版本库中的文件也删除 git rm test.txt,且提交commit;删错了,可以从版本库中恢复该文件git checkout -- test.txt

git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

④远程仓库

  • 将本地仓库和GitHub上的仓库合并,并且同步。先在GitHub上创建一个仓库。在本地仓库中输入以下命令:$ git remote add origin git@github.com:fangpeng/learngit.git,其中fangpeng为GitHub的账户名。添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
  • 将本地仓库中的内容推送到远程库上。把本地库的内容推送到远程,$ git push -u origin master,实际上是把当前分支master推送到远程。加上-u参数可以让本地的master分支和远程的master分支关联起来,在以后的推送和拉取时就可以简化命令。
  • 查看远程库地址信息:$ git remote -v 删除远程库:$ git remote rm origin
  • 克隆远程库:$ git clone git@github.com:fangpeng/learngit.git
  • 将远程仓库的分支拉下来:git fetch,再合并git merge。这两个操作等同于git pull

⑤分支管理

  • 切换到dev分支:$ git checkout dev
  • 创建并切换:$ git checkout -b dev
  • 查看当前分支:$ git branch
  • dev分支合并到master分支:$ git merge dev
  • 删除dev分支:$ git branch -d dev
  • 查看分支的合并情况:$ git log --graph
posted @ 2021-09-23 19:49  FpengtoJava  阅读(45)  评论(0)    收藏  举报