Git基本概念及操作(3)

发布时间:2019-09-02 07:42:39编辑:auto阅读(1690)

    如果使用传统的如CC开发的话,刚开始进行GIT开发可能不是太适应。这个主要是有些概念不一样。比喻在CC中,我们一般是围绕一个主分支进行开发,对一个文件来说,在主分支上会生成不同的版本。同样,我们在每一个版本下面创立新的次分支,在次分支上也会生出很多版本。最后合到主分支,产生下一个版本。那么在GIT中是如何实现这些关联呢?GIT中同样有分支、版本概念。但是没有Configspec概念。tag概念同LABEL概念类似。当然这些概念都同GIT中是如何管理文件版本相关的。首先我们来看GIT是如何将文件对象化管理的,前面我们说GIT同其它版本管理系统不一样是GIT每个版本都不是保存变更,而是全保存。那么如果全保存的话,显然会带来相当大的硬盘开销,其弊端非常明显,那么GIT是怎么消除这个弊端的呢?GIT利用了HASH算法,我们知道在目前已知道的算法中,HASH(SHA-1)算法产生的唯一性还是非常强的。也就是说虽然在工作区域是一个普通文件,但是在仓库中保存的是一个HASH值,由这个HASH值来表示文件,自然空间节省很多。GIT中将这个HASH值称之为对象。这些对象通常是由提交版本、子目录、文件的HASH值组成。对每一个对象通常按类型、大小和内容进行管理。其中最主要的是类型分为三种:

    1)blob 这种类型对象是用来存储文件数据,GIT中表现为一个HASH值,如下图所示:

    image

    在以前GIT版本中会有一个单独的目录保存这些文件生成的HASH值通常是.git/objects/fc现在不在有这个fc目录,直接取生成的HASH的前两个值作目录。如下所示,注意在未提交之前,目录是不诞生对象。

    [root@wrlinux3 mygit]# git add .
    [root@wrlinux3 mygit]# ls -latr .git/objects/
    total 28
    drwxr-xr-x. 2 root root 4096 Apr 28 13:34 pack
    drwxr-xr-x. 2 root root 4096 Apr 28 13:34 info
    drwxr-xr-x. 2 root root 4096 Apr 28 13:35 f8
    drwxr-xr-x. 2 root root 4096 Apr 28 13:35 d2
    drwxr-xr-x. 2 root root 4096 Apr 28 13:35 52
    drwxr-xr-x. 7 root root 4096 Apr 28 13:35 ..
    drwxr-xr-x. 7 root root 4096 Apr 28 13:35 .
    [root@wrlinux3 mygit]# git hash-object README
    52c897fedf9f8728e953a149b7be3b5829a07b1c
    [root@wrlinux3 mygit]# git hash-object main.c
    f8b643afbf2c84dc03b777743d3e53a22045cf49
    [root@wrlinux3 mygit]# git hash-object testdir/test.c
    d22409509cd2f0a420e0cb6355008a9676656961

    [root@wrlinux3 mygit]# git show d224
    int test()
    {
    return 0;
    }
    [root@wrlinux3 mygit]# git show f8b6
    int main()
    {
      return 0;
    }
    [root@wrlinux3 mygit]# git show 52c8
    new readme

    [root@wrlinux3 mygit]# git cat-file -t 52c8
    blob
    [root@wrlinux3 mygit]# git cat-file -t f8b6
    blob
    [root@wrlinux3 mygit]# git cat-file -t d224
    blob

    2)tree 是一个simple 对象,它的内容就是一个指针表,指针指向的就是这个目录下的所有文件及子目录对象。也就是说这个对象记录了当前目录的元信息。如下图所示:

    image

    正如前面所说,tree也是一串HASH值,这串HASH存储的是元信息。用来表示目录层次关系的。这和一般的SCM系统中是不一样,一般的SCM中目录同文件是一样的保存为版本对象的。

    [root@wrlinux3 mygit]# git commit -a -m "init commit"
    [master (root-commit) 4cfc524] init commit
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    3 files changed, 9 insertions(+)
    create mode 100644 README
    create mode 100644 main.c
    create mode 100644 testdir/test.c
    [root@wrlinux3 mygit]# ls -latr .git/objects/
    total 40
    drwxr-xr-x.  2 root root 4096 Apr 28 13:34 pack
    drwxr-xr-x.  2 root root 4096 Apr 28 13:34 info
    drwxr-xr-x.  2 root root 4096 Apr 28 13:35 f8
    drwxr-xr-x.  2 root root 4096 Apr 28 13:35 d2
    drwxr-xr-x.  2 root root 4096 Apr 28 13:35 52
    drwxr-xr-x.  2 root root 4096 Apr 28 14:12 bc
    drwxr-xr-x.  2 root root 4096 Apr 28 14:12 a8
    drwxr-xr-x.  2 root root 4096 Apr 28 14:12 4c
    drwxr-xr-x. 10 root root 4096 Apr 28 14:12 .
    drwxr-xr-x.  8 root root 4096 Apr 28 14:12 ..
    [root@wrlinux3 mygit]# ls -latr .git/objects/4c/fc5245433ac0b9277892ccb6e7bd4347aa01ec
    -r--r--r--. 1 root root 133 Apr 28 14:12 .git/objects/4c/fc5245433ac0b9277892ccb6e7bd4347aa01ec
    [root@wrlinux3 mygit]# ls -latr .git/objects/a8/8383ae64b09e71666adc12f4bf4760857f8c55
    -r--r--r--. 1 root root 115 Apr 28 14:12 .git/objects/a8/8383ae64b09e71666adc12f4bf4760857f8c55
    [root@wrlinux3 mygit]# ls -latr .git/objects/bc/a9ca7434467d22451fc370375dbab1a8930433
    -r--r--r--. 1 root root 51 Apr 28 14:12 .git/objects/bc/a9ca7434467d22451fc370375dbab1a8930433
    [root@wrlinux3 mygit]# git cat-file -t bca9
    tree
    [root@wrlinux3 mygit]# git cat-file -t a883
    tree
    [root@wrlinux3 mygit]# git cat-file -t 4cfc
    commit
    [root@wrlinux3 mygit]# git ls-tree bcag
    fatal: Not a valid object name bcag
    [root@wrlinux3 mygit]# git ls-tree bca9
    100644 blob d22409509cd2f0a420e0cb6355008a9676656961    test.c
    [root@wrlinux3 mygit]# git ls-tree a883
    100644 blob 52c897fedf9f8728e953a149b7be3b5829a07b1c    README
    100644 blob f8b643afbf2c84dc03b777743d3e53a22045cf49    main.c
    040000 tree bca9ca7434467d22451fc370375dbab1a8930433    testdir
    [root@wrlinux3 mygit]#

    3)commit 也是一种对象,但是这种对象存储是在提交这个点上的元信息,包括提交人、上次提交的版本对象指针及本次提交包含的树节点指针。如下图所示:

    image

    看到这里可能还不是很明白。那我们将面连接起来如下所示:

    image

    从这个图我们应该能够简单的明白了,在GIT中版本这种对象是在最高层,不像一般SCM中,对一个文件来说会记录不同的版本。而在GIT中不是这样的,它是对当下这个项目提交一个版本就进行一次快照。通俗的说就是一个版本包含当前快照下所有对象指针。这样从版本管理的角度来说,要管理的对象的是版本。不再是文件或者目录。这些东西统统可以缩成一个快照。如下图所示:

    image

    在实际上可以使用如下命令查看:

    [root@wrlinux3 mygit]# git show -s --pretty=raw 4cfc
    commit 4cfc5245433ac0b9277892ccb6e7bd4347aa01ec
    tree a88383ae64b09e71666adc12f4bf4760857f8c55
    author ROOT root <root@wrlinux3.nsn-nsn.net> 1335593557 +0800
    committer ROOT root <root@wrlinux3.nsn-nsn.net> 1335593557 +0800

        init commit
    [root@wrlinux3 mygit]#

    通过上面的讲述我相信在Commit、Tree和Blob对象之三者的关系有了一个简单的认识,有人总结成了如下一张图,可以说是GIT实现的核心。

    image

    从这张图我们也就明白了前面所说了Commit对象作为GIT管理的核心。GIT的Commit对象它不是说针对单个文件,它是针对的整个项目的。另外还有一种对象,TAG对象。TAG对象是用来标记特定的Commit的,如下图所示,这里可能还不能够清楚解析,后面学习了分支之后会比较清楚的了解。

    image

    前面讲述了四种GIT对象,GIT对象是不可改变的(除了一些撤消操作之外),当然这里面的TAG对象有时候也可以称之为References引用,引用是可以移动,引用都是基本版本Commit对象的。因此从另外一个角度来说TAG也可以称之为引用。但实际上我们打了一个TAG之后是不能移动,他的特性与对象又一样。常用的引用主要分为分支、头、及远程分支。如下图所示:

    image

    从上图可以看出,HEAD,BRACH,REMOTE,TAG最下层都是Commit对象,也就是GIT管理的粒度是以Commit来决定。Commit相当于一个快照。我们可以从git目录查看到这种组织方式。

    [root@wrlinux3 mygit]# cat .git/HEAD
    ref: refs/heads/master
    [root@wrlinux3 mygit]# ls -l .git/refs/
    total 8
    drwxr-xr-x. 2 root root 4096 Apr 28 14:12 heads
    drwxr-xr-x. 2 root root 4096 Apr 28 13:34 tags
    [root@wrlinux3 mygit]# ls -l .git/refs/tags/
    total 0
    [root@wrlinux3 mygit]# ls -l .git/refs/heads/
    total 4
    -rw-r--r--. 1 root root 41 Apr 28 14:12 master
    [root@wrlinux3 mygit]# cat .git/refs/heads/master
    4cfc5245433ac0b9277892ccb6e7bd4347aa01ec
    [root@wrlinux3 mygit]# git cat-file -t 4cfc
    commit
    [root@wrlinux3 mygit]#

    另外GIT也使用了优化算法,就是前面两个版本之间如果相同的对象没有发生改变的话,会直接采用链接的方式。如下图所示:

    image

    这样就可以省去很多对象的产生。前面我们对Reference对象没有特别强调,只是强调他们是可以随着分支移动。这一点与很多SCM系统不同,比喻说CC,分支拉出来就固定,分支上生长出版本。而GIT不同,GIT上的分支它是随着版本移动的。我们来研究如下:

    1)分支 GIT中分支本质上是个指向Commit 对象的可变分支。默认情况下Git使用master作为分支的默认名字,每次一提交,master指针都会自动向前移动到下一个版本的commit对象上。当然你也可创建一个分支,使用命令git branch testing.如下图所示:

    image

    可以通过仓库显示如下:

    [root@wrlinux3 mygit]# git branch testing
    [root@wrlinux3 mygit]# git branch
    * master
      testing

    [root@wrlinux3 mygit]# cat .git/HEAD
    ref: refs/heads/master

    [root@wrlinux3 mygit]# cat .git/refs/heads/master
    4cfc5245433ac0b9277892ccb6e7bd4347aa01ec
    [root@wrlinux3 mygit]# cat .git/refs/heads/testing
    4cfc5245433ac0b9277892ccb6e7bd4347aa01ec
    [root@wrlinux3 mygit]#

    这时我们可以看master和testing都指向4cfc Commit对象,同时master前面带有*号,HEAD中指向master,表示当前工作分支还在master上,如果想要在testing上工作,就必须修改HEAD指针。使用git checkout testing就可以切换了。

    [root@wrlinux3 mygit]# git checkout testing
    Switched to branch 'testing'
    [root@wrlinux3 mygit]# cat .git/HEAD
    ref: refs/heads/testing
    [root@wrlinux3 mygit]#

    这时HEAD指针指向Testing分支了。

    image

    因为在GIT中创建和删除分支基本上不需要耗费计算机资源,因为分支在GIT表示的就是一个指向版本的指针(这个文件中保存的是版本对像的SHA值)。所以GIT是非常鼓励多建分支。那么很明显分支太多,也会带来版本合并的麻烦,所以一般来说建议尝试一个新功能在一个分支上开发完毕后,进行测试完成后就合并到主分支上再删除原来分支。下面我们围绕一个开发过程来进行分支的创建和合并。

    image

    对应的版本图如下:

    image

    对应的操作记录如下:

    [root@wrlinux3 mygit]# git checkout -b iss53
    Switched to a new branch 'iss53'
    [root@wrlinux3 mygit]# vi main.c
    [root@wrlinux3 mygit]# git commit -a -m 'add new variable to calculate the value on branch [iss53]'
    [iss53 2f1f64e] add new variable to calculate the value on branch [iss53]
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    1 file changed, 5 insertions(+), 1 deletion(-)
    [root@wrlinux3 mygit]# git checkout master
    Switched to branch 'master'
    [root@wrlinux3 mygit]# git checkout -b 'hotfix'
    Switched to a new branch 'hotfix'
    [root@wrlinux3 mygit]# git branch
    * hotfix
      iss53
      master
      testing
    [root@wrlinux3 mygit]# cd testdir/
    [root@wrlinux3 testdir]# vi test.c
    [root@wrlinux3 testdir]# git commit -a -m 'fixed the issue"
    [root@wrlinux3 testdir]# cd ..
    > ls
    > ^C
    [root@wrlinux3 testdir]# cd ..
    [root@wrlinux3 mygit]# ls
    main.c  README  testdir
    [root@wrlinux3 mygit]# git commit -a -m 'fixed the issue"
    > ^C
    [root@wrlinux3 mygit]# git commit -a -m 'fixed the issue'
    [hotfix 85412f9] fixed the issue
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    1 file changed, 4 insertions(+), 1 deletion(-)
    [root@wrlinux3 mygit]# git checkout master
    Switched to branch 'master'
    [root@wrlinux3 mygit]# git merge hotfix
    Updating 4cfc524..85412f9
    Fast-forward
    testdir/test.c |    5 ++++-
    1 file changed, 4 insertions(+), 1 deletion(-)
    [root@wrlinux3 mygit]# git branch -d hotfix
    Deleted branch hotfix (was 85412f9).
    [root@wrlinux3 mygit]# git branch
      iss53
    * master
      testing
    [root@wrlinux3 mygit]# vi testdir/test.c
    [root@wrlinux3 mygit]# git checkout master
    Already on 'master'
    [root@wrlinux3 mygit]# git merge iss53
    Merge made by the 'recursive' strategy.
    main.c |    6 +++++-
    1 file changed, 5 insertions(+), 1 deletion(-)
    [root@wrlinux3 mygit]# git merge iss53
    Already up-to-date.
    [root@wrlinux3 mygit]#

    通过GIT进行合并,因为是基本文本方式合并,可能不与CC中图形化直观,但是一定要注意两点,一点是DIFF工具可改,二点是MERGE前的工作区应该是干净的。当提示一些CONFLIC时需要手工打这些文件进行修改。

    [root@wrlinux3 mygit]# git mergetool
    No files need merging
    [root@wrlinux3 mygit]# git checkout testing
    Switched to branch 'testing'
    [root@wrlinux3 mygit]# ls
    main.c  README  testdir
    [root@wrlinux3 mygit]# vi main.c
    [root@wrlinux3 mygit]# git commit -a -m "test comments"
    [testing d2569cb] test comments
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    1 file changed, 1 insertion(+)
    [root@wrlinux3 mygit]# git checkout master
    Switched to branch 'master'
    [root@wrlinux3 mygit]# git mergetool
    No files need merging
    [root@wrlinux3 mygit]# git merge testing
    Auto-merging main.c
    CONFLICT (content): Merge conflict in main.c
    Automatic merge failed; fix conflicts and then commit the result.
    [root@wrlinux3 mygit]# git status
    # On branch master
    # Unmerged paths:
    #   (use "git add/rm <file>..." as appropriate to mark resolution)
    #
    #    both modified:      main.c
    #
    no changes added to commit (use "git add" and/or "git commit -a")
    [root@wrlinux3 mygit]# git merge testing
    error: 'merge' is not possible because you have unmerged files.
    hint: Fix them up in the work tree,
    hint: and then use 'git add/rm <file>' as
    hint: appropriate to mark resolution and make a commit,
    hint: or use 'git commit -a'.
    fatal: Exiting because of an unresolved conflict.
    [root@wrlinux3 mygit]# git status
    # On branch master
    # Unmerged paths:
    #   (use "git add/rm <file>..." as appropriate to mark resolution)
    #
    #    both modified:      main.c
    #
    no changes added to commit (use "git add" and/or "git commit -a")
    [root@wrlinux3 mygit]# git commit -a -m "commit stat"
    [master 5e453fa] commit stat
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    [root@wrlinux3 mygit]# git st
    # On branch master
    nothing to commit (working directory clean)

    前面讲了一些通用的分支操作,通过创建分支、合并分支并最终形成一个稳定的版本的过程,这个过程也是我们常见的SCM的管理职责,目前SCM人员需要制定主分支、开发分支、特性分支,通过管控这些分支来保证整个产品的质量的稳定。如下图所示:

    image

    上图中不同的开发分支,代表了产品成熟的不同的周期。通过维护这些分支的生命周期来维护产品的生命周期。这里还需要强调的我们前面所进行的操作都是向有操作,也就是MERGE操作,实际上还有一种操作叫REBASE操作,rebase比较复杂,轻易不要使用,因为rebase会将当前已提交的版本历史给删除掉了,这样的话,一旦别人根据这些开发的话会产生丢失版本情况。如下图所示:

    image

    对应的操作记录如下:

    [root@wrlinux3 mygit]# git branch experiment
    [root@wrlinux3 mygit]# git branch
      experiment
    * master
    [root@wrlinux3 mygit]# git checkout -b experiment
    fatal: A branch named 'experiment' already exists.
    [root@wrlinux3 mygit]# git checkout -B experiment
    Switched to and reset branch 'experiment'
    [root@wrlinux3 mygit]# vi main.c
    [root@wrlinux3 mygit]# git commit -a -m 'commit on experiment'
    [experiment 774b7ee] commit on experiment
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    1 file changed, 2 insertions(+), 3 deletions(-)
    [root@wrlinux3 mygit]# git checkout master
    Switched to branch 'master'
    [root@wrlinux3 mygit]# vi main.c
    [root@wrlinux3 mygit]# git commit -a -m 'commit on master'
    [master cbe0f99] commit on master
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    1 file changed, 1 insertion(+), 2 deletions(-)
    [root@wrlinux3 mygit]# gitk
    [root@wrlinux3 mygit]# vi main.c
    [root@wrlinux3 mygit]# git commit -a -m 'commit on master more'
    [master 8f20d8c] commit on master more
    Committer: ROOT root <root@wrlinux3.nsn-nsn.net>
    Your name and email address were configured automatically based
    on your username and hostname. Please check that they are accurate.
    You can suppress this message by setting them explicitly:

        git config --global user.name "Your Name"
        git config --global user.email you@example.com

    After doing this, you may fix the identity used for this commit with:

        git commit --amend --reset-author

    1 file changed, 1 insertion(+)
    [root@wrlinux3 mygit]# git checkout experiment
    Switched to branch 'experiment'
    [root@wrlinux3 mygit]# git rebase master

    It seems that there is already a rebase-apply directory, and
    I wonder if you are in the middle of another rebase.  If that is the
    case, please try
        git rebase (--continue | --abort | --skip)
    If that is not the case, please
        rm -fr /work/bongos/mygit/.git/rebase-apply
    and run me again.  I am stopping in case you still have something
    valuable there.
    [root@wrlinux3 mygit]# rm -fr /work/bongos/mygit/.git/rebase-apply
    [root@wrlinux3 mygit]# git rebase master
    First, rewinding head to replay your work on top of it...
    Applying: commit on experiment
    Using index info to reconstruct a base tree...
    Falling back to patching base and 3-way merge...
    Auto-merging main.c
    CONFLICT (content): Merge conflict in main.c
    Failed to merge in the changes.
    Patch failed at 0001 commit on experiment

    When you have resolved this problem run "git rebase --continue".
    If you would prefer to skip this patch, instead run "git rebase --skip".
    To check out the original branch and stop rebasing run "git rebase --abort".

    [root@wrlinux3 mygit]# vi main.c
    [root@wrlinux3 mygit]# git rebase --continue
    main.c: needs merge
    You must edit all merge conflicts and then
    mark them as resolved using git add
    [root@wrlinux3 mygit]# git add .
    [root@wrlinux3 mygit]# git rebase --continue
    Applying: commit on experiment
    [root@wrlinux3 mygit]# git st
    # On branch experiment
    nothing to commit (working directory clean)
    [root@wrlinux3 mygit]# gitk
    [root@wrlinux3 mygit]# git branch
    * experiment
      master
    [root@wrlinux3 mygit]#

关键字

上一篇: 6.简单sql注入之3

下一篇: H3C设备之ARP代理