Git基本原理和操作

 

前言:丢失代码的惨痛经历

这是一次在分支切换时丢失工作区内容的惨痛经历。
刚开始直接就在分支pre上进行开发工作,然后需要切换到dev分支上进行开发

git checkout dev

此时pre上工作区的内容因为和dev没有冲突,所以工作区的内容完整保留到dev中。
然后想把dev上的工作区内容删除掉(注意这是错误理解,事实上git工作区和暂存区并不分支),因此用版本回退命令git reset head --hard,意图清除dev上(注意,错误理解!)工作区和暂存区新的代码

git reset head --hard //这是把自己往死里逼啊

然而当再切回到pre上时,发现工作区和暂存区的新代码也消失了。
既然发现git的路走弯了,那么跪着也要把它掰回来 :) 。因此事后对git的原理和常见操作进行了一次完整的梳理。
这里先不讲工作区代码丢失惨案的解决方案,请大家自行思考原因和解决方案。文章最后我会揭晓答案。

一、Git基本概念

1.分区的概念及意义

git分三个区,工作区(working tree),暂存区(indexing),版本库(repository)。

  • 工作区:是开发者书写代码的地方。
  • 暂存区:工作区的内容若想提交到版本库,需要先提交到暂存区。暂存区用来保证部分功能提交,不用所有书写的代码一起提交,而是根据功能点有节奏的提交。
  • 版本库:每个project都有一个本地版本库,利于分布式版本控制,避免服务端单点故障问题。另外可以客户端与客户端之间实现代码clone。 

 

2.分支的概念及意义

与svn不同,svn开辟一个分支会拷贝代码,而git仅仅是增加一个指针而已,轻体瘦身。

HEAD的概念:指向当前分支最新的commit点。

二、初始化操作

  • git init 
    初始化本地仓库。在项目的根目录下运行该命令,那么该project根目录下就会生成.git文件,并将该project纳入版本管理,.git文件中拥有所有的版本控制信息,删除了该文件,那么git便失去了所有的本地版本记录。
//.git目录中的内容
GccJ:.git zhangwenhao$ ls -la
total 24
drwxr-xr-x  10 zhangwenhao  wheel  320 Nov  5 14:56 .
drwxr-xr-x   6 zhangwenhao  wheel  192 Nov  5 14:55 ..
-rw-r--r--   1 zhangwenhao  wheel   23 Nov  5 14:55 HEAD
drwxr-xr-x   2 zhangwenhao  wheel   64 Nov  5 14:55 branches
-rw-r--r--   1 zhangwenhao  wheel  164 Nov  5 14:56 config
-rw-r--r--   1 zhangwenhao  wheel   73 Nov  5 14:55 description
drwxr-xr-x  13 zhangwenhao  wheel  416 Nov  5 14:55 hooks
drwxr-xr-x   3 zhangwenhao  wheel   96 Nov  5 14:55 info
drwxr-xr-x   4 zhangwenhao  wheel  128 Nov  5 14:55 objects
drwxr-xr-x   4 zhangwenhao  wheel  128 Nov  5 14:55 refs
  • git clone
    拷贝仓库的内容,可以拷贝别人客户端的,也可以拷贝服务端的。
//拷贝远程服务端仓库
//首先进入你的项目想要在的目录中,运行如下命令
git clone https://github.com/git-share-study/git-share-study.git

//拷贝别人客户端仓库
//首先进入你的项目想要在的目录中,运行如下命令
git clone zhangwenhao@192.168.44.38:/myCode/IdeaProjects/git-share-study
  • git config 
    修改git的配置文件,其中--system是系统级别配置,--global是当前用户配置,--local是当前项目的配置。各个配置文件位置,可以通过git config --help查看。
//设置git用户名,用于git登陆
git config --global user.name "zhangwenhao"
//设置git邮箱账号,用于git登陆
git config --global user.email "zhangwenhao@peilian.com"
//设置快捷键co替代checkout
git config --global alias.co "checkout"
//设置快捷键cm替代commit -m
git config --global alias.cm "commit -m"

//查看配置内容
git config --global -l

//结果
user.name=zhangwenhao
user.email=zhangwenhao@peilian.com
alias.co=checkout
alias.cm=commit -m
  • git remote add 
    增加远程仓库服务器的地址。
//添加远程仓库地址源,并起别名rr
git remote add rr https://github.com/git-share-study/git-share-study.git

//查看远程仓库地址
git remote -v
//结果
rr      https://github.com/git-share-study/git-share-study.git (fetch)
rr      https://github.com/git-share-study/git-share-study.git (push)

//删除远程仓库地址
git remote remove rr

三、单分支各区之间的操作(工作区、暂存区、版本库)

  • git status
    查看工作区,暂存区文件的状态!
  • git add [file]
    工作去内容添加到暂存区!其中为.的话代表所有文件。
  • git commit -m "msg"
    暂存区所有内容添加到本地仓库!
  • git checkout [file]/git restore [file]
    从暂存区拉取文件,覆盖工作区
//将暂存区的haha文件覆盖工作区的haha文件
git checkout haha
或者
git restore haha
  • git reset head /git restore --staged [path]
    从版本库拉取文件,覆盖暂存区
//将版本库的head指向版本的内容覆盖暂存区内容
git reset head
//将版本库最新版的haha文件覆盖暂存区的haha文件
git restore --staged haha
  • git reset head --hard 
    从版本库拉取最新提交点,并覆盖暂存区和工作区。hard的作用,就是一同覆盖暂存区和工作区,不用hard,那么只会覆盖暂存区。

  • git rm [file]
    git rm是从暂存区和工作区中删除文件!
    rm命令仅仅删除工作区的文件!
    git rm --cached 那么就仅仅删除工作区的内容!
    可以通过上述的checkout或restore命令进行内容恢复!如果要删除文件夹,那么需要加入-r参数,表示递归删除。

  • git help
    查看基本常用命令行!

  • git stash
    将工作区的内容存放起来,在需要的时候取出!
    开发者进行了代码的更新,只要最终没有commit到版本库的内容都可以stash起来。

//添加到stash库中,save后是备注信息
git stash save "firt time"或git stash
//查看stash库列表
git stash list
//结果
stash@{0}: On master: firt time

//切换到其他分支(也可以不切换)
git checkout dev
//在需要的任何时间,任何分支,操作如下指令,即可加入到工作区中(如有冲突,解决即可)
git stash apply stash@{0}

//删除某个stash内容
git stash drop stash@{0}
//清空所有stash内容
git stash clear
  • git reset --hard HEAD^ 
    等同于git reset --hard HEAD~1,即拉取最新提交点再往前1个(根据^ 的数量或者~后的数字进行决定)提交点拉取文件并覆盖暂存区和工作区。
  • git reset --hard 6ca340b(commit id)
    从版本id拉取文件,覆盖暂存区和工作区。
  • git log
    查看commit提交日志。用户git reset commitId进行版本回退。注意版本回退后,就无法看到回退点之前的commitId咯,也就无法进行版本前进。至于如何解决,请看下面的git reflog。
//git log 结果
commit ff780e5b16c795840a602f1bdf05f261c473b48a (HEAD -> dev, master)
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Tue Nov 5 17:19:21 2019 +0800

    ignore

commit 6ca340ba642285757028dbfe3ce004346b1481d5
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Tue Nov 5 16:34:41 2019 +0800

    skd

commit 491db9dbf995d8feeb5dd552ef9fdae141058f1f
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Tue Nov 5 16:33:33 2019 +0800

    rm

  • git reflog
    查看所有操作的日志。因为当用git reset版本回退后,用git log 就无法看到head前面的日志了,此时可以通过git reflog查看重要操作点的commitId ,进行版本再次前进,版本前后左右随心切换的快感,就在于此。
//git reflog的结果
ff780e5 (HEAD -> dev, master) HEAD@{0}: reset: moving to ff780e5
ff780e5 (HEAD -> dev, master) HEAD@{1}: reset: moving to ff780e5
491db9d HEAD@{2}: reset: moving to head~2
ff780e5 (HEAD -> dev, master) HEAD@{3}: reset: moving to ff780e5
491db9d HEAD@{4}: reset: moving to head^^
ff780e5 (HEAD -> dev, master) HEAD@{5}: checkout: moving from master to dev
ff780e5 (HEAD -> dev, master) HEAD@{6}: reset: moving to HEAD
ff780e5 (HEAD -> dev, master) HEAD@{7}: reset: moving to head
ff780e5 (HEAD -> dev, master) HEAD@{8}: commit: ignore

  • git rebase 提交点合并
    有时候一个功能开发提交后,多次发现问题,又进行了多次提交,那么整个分支的提交记录就会零散而混乱,如何将多次修改过程的合并呢?就用git rebase功能,rebase的意思就是重新选择基础。
    首先用git log 查看提交点:
commit bb7fa1fd4421789de155e525257a500941f3b815 (HEAD -> dev)
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Thu Nov 7 18:29:43 2019 +0800

    2

commit 500b0832b66499b825ee1dff8dbd31de5ddf97a2
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Thu Nov 7 18:29:29 2019 +0800

    1

commit 8dc44dd1f9b15fab14e2c0ba478612609f9d6042
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Thu Nov 7 18:22:34 2019 +0800

我们需要将最近的两次提交点合并,所以应该选择8dc44dd1f9b15fab14e2c0ba478612609f9d6042作为其新的基础点,因此调用

git rebase -i 8dc44dd1f9b15fab14e2c0ba478612609f9d6042

弹出配置框,并配置squash id1和pick id2,表示将id1和id2的提交点进行合并

pick 500b083 
squash bb7fa1f 

# Rebase 8dc44dd..bb7fa1f onto 8dc44dd (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit

保存后会跳出填写combination的提交信息,填写后再次保存,然后查看提交记录:

commit 18e88fce095f5464e388b907e6466c1377738b7b (HEAD -> dev)
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Thu Nov 7 18:29:29 2019 +0800

    this ia a combination
    1
    
    2

commit 8dc44dd1f9b15fab14e2c0ba478612609f9d6042
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Thu Nov 7 18:22:34 2019 +0800

即发现多个提交点合并成了一个提交点。

 

四、多分支之间操作

  • git branch -vv
    查看所有本地分支。-v即verbose的意思
  • git branch -c dev
    从所在分支复制并创建新的分支dev(但不切换到该分支),或者用git checkout -b dev,复制创建新的分支,并切换到该分支。-c即copy的意思,-b即branch的意思。
  • git branch -d dev
    删除本地分支dev(你必须在非dev分支才可以这么操作),如果-d改成-D,那么就是无视警告,强制删除dev分支。
  • git checkout master
    从该分支版本库最新提交点拉取数据,并覆盖工作区和暂存区。

五、本地仓库和远程仓库之间操作

1.git remote对数据源的操作

  • git remote add origin [url]
    在本地配置远程仓库源,并起别名origin。此时未和远程仓库建立追踪关系。
//创建名为origin的远程仓库源
git remote add origin https://github.com/git-share-study/git-share-study.git
  • git remote rename [oldname] [newname]
    重命名本地配置的远程数据源。此时未和远程仓库建立追踪关系。
//将origin的数据源名称改为ddd
git remote rename origin ddd
  • git remote remove [remotename]
    删除本地配置的远程数据源。此时未和远程仓库建立追踪关系。
//删除名称为ddd的数据源
 git remote remove ddd
  • git remote -v
    查看本地配置的远程数据源情况。此时未和远程仓库建立追踪关系。
//查看数据源情况
git remote -vv
//结果
origin  https://github.com/git-share-study/git-share-study.git (fetch)
origin  https://github.com/git-share-study/git-share-study.git (push)

2.git branch对数据源的追踪操作

  • git branch --set-upstream-to=origin/dev
    设置本地分支默认操作对应的远程仓库分支(前提必须要先track到远程仓库),可以用git branch --unset-upstream 来解除默认操作。当设置完毕后,上述两个命令只需简单输入git push 或git pull操作即可默认从本地推送到设置的远程分支上,从而不用指定远程仓库信息。

  • git branch -r --list
    查看远程已经追踪的分支(注意是追踪的分支,即和本地有关联的分支,通过git pull,git push,git fetch建立和远程仓库的追踪关系)

  • git branch -r -d origin/pre
    删除远程分支追踪关系

3.push/pull对远程仓库的操作

  • git fetch 
    将远程仓库的内容拉到本地,并放入一个临时的名称为FETCH_HEAD分支上。此时本地和远程建立了track关系。

  • git pull 
    从远程仓库拉取到本地仓库,并merge到该分支,覆盖版本库,暂存区,工作区。此时本地和远程建立了track关系。等同于下面两步骤。

    git fetch 
    然后
    git merge FETCH_HEAD
    

    git pull origin master 从master拉取到本地当前分支
    git pull origin master:pre 从master拉取到本地pre分支
    如果在pull的时候出现refusing to merge unrelated histories报错,那么加上--allow-unrelated-histories参数即可。

  • git push 
    将本地版本库内容推送到远程仓库。此时本地和远程建立了track关系。
    git push origin master 从本地当前分支推送到远程master分支
    git push origin master:pre 从本地master分支推送到远程pre分支
    如果第一次将本地仓库推送到远端空仓库(因为是空仓库,所以无法通过git fetch,git pull建立track关系),那么要加一个-u来建立track关系(等同于--set-upstream),git push -u origin master。

  • git push origin --delete pre
    删除远程分支

4.git tag标签管理

标签管理就是记录一些重要关键点的commitId,比如在发布版本的时候,进行一个tag记录,可以更方便更清晰的管理代码版本。

  • git tag [name]
    创建一个标签,可以用git tag -a [name] -m 'msg',来建立一个有备注的标签,-a即annotate,-m即message
//创建一个简单的标签
git tag v1.0
//创建一个带备注的标签
git tag -a -m 'firt release 2019/10/10' v1.1
  • git tag -l 'regex'
    展示标签列表,'regex'代表查询条件
//精确查询
git tag -l 'v1.0'
//*即为通配符,代表以1结尾的tag名
git tag -l '*1'
//*即为通配符,代表包含1的tag名
git tag -l '*1*'

//结果
v1.0
v1.1
  • git show [tagname]
    展示tag的详细信息,通过其中的commitId可以进行版本回退等操作哦!
//展示标签v2.0
git show v2.0

//结果
tag v2.0
Tagger: zhangwenhao <zhangwenhao@peilian.com>
Date:   Thu Nov 7 16:17:31 2019 +0800

second tag

commit 69d644839fc17e18d40d21d4a14005e0083b1024 (HEAD -> master, tag: v2.0, origin/master)
Author: zhangwenhao <zhangwenhao@peilian.com>
Date:   Tue Nov 5 18:41:33 2019 +0800

    ha

diff --git a/haha b/haha
index c82fdc3..38e28f7 100644
--- a/haha
+++ b/haha
@@ -1 +1,2 @@
-skdfj

  • git tag -d [name]
    删除标签
  • git push origin [name1] [name2] ..
    上传标签,可以指定多个名称,若想上传本地所有标签,使用git push origin --tags,在远程仓库的release页面中即可看到标签列表:

项目组的其他人再次git pull远程仓库的同时,会拉取远程仓库中本地所没有的标签。就可以同步标签信息了。

六、几种常见的git问题解决

1.远程仓库上传了多余的文件

场景:.gitignore忘记过滤了.idea文件,并且上传到了远程仓库,如何删除远程仓库里的.idea,并且将该文件重新加入ignore文件。
需要明白,.idea文件已经加入的本地版本库和暂存区,而.gitignore仅仅对尚未加入暂存区和本地版本库的内容起作用,因此,首先要做的是删除本地暂存区,版本库的内容,然后删除远程仓库的内容,再添加到过滤文件中:

//删除暂存区的内容(工作区需要保留)
git rm -r --cached .idea
//删除本地版本库中的内容,cm在前面设置的alias,相当于commit -m
git cm "del"
//删除远程仓库中的内容(已经用git push --set-upstream origin dev设置过默认源,因此可以用git push这种简便操作)
git push

//在.gitingnore文件中加入如下规则
.idea

2.本地版本比远程版本落后

场景:本地版本回退,然后修改提交,此时git push操作会rejected。
此时需要删除远程仓库的分支,然后重新push上去分支即可。(其他人的客户端如果想要上传,必须先pull,解决冲突后,才能push)

//先删除远程分支
git push origin dev -d
//再推送回退版本后并修改的版本库
git push

七、揭露开始谜题

刚开始提到的问题,工作区的内容丢失该如何应对呢?
第1种方法:可以用本地历史找回文件的记录点,但是其保存点是随机的,有可能漏掉关键代码。因此不推荐。
第2种方法:先commit保存下来,再去另一个分支开发,不推荐,因为每一个保存点都应当是一个完整的功能点,都应该是完整开发并测试后的功能点,便于以后查看、回退、开新分支等操作,不能将版本库当作一个保存文件的地方。
第3种方法:用git stash 保存下来,然后切换到别的分支去开发,当需要继续回来开发时,再用git stash apply stash{index}重新提取到工作区内。(完整操作看第3章中的git stash介绍)这种方法是最可取的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值