• git常用命令,源码分析及在IDEA中使用


    前言

    git是一个免费的开源分布式版本控制系统,它最初是 Linus Torvalds 于2005 年 4 月,为了帮助管理 Linux 内核开发而开发的版本控制软件

    ​ 版本控制系统(Version Control System, VCS)是一种可以记录一个或多个文件内容变化,以便将来查阅的系统。它有四个发展阶段:

    1. 起源

      ​ linux有两个工具diff和patch,可以计算两个文件的不同,并还原。这两个工具可以说是VCS的起源。据说1991-2002年之间,即使CVS出现之后,Linus一直使用diff和patch管理着Linux的代码

    2. 第一代:本地版本控制系统

      ​ 1982 年开发的修订控制系统(Revision Control System,RCS) 是第一代的版本控制系统,它由一组 UNIX 命令构成。RCS把diff的集合,通过自己的格式保存到磁盘中,还可以通过这些diff集合,重新回到文件修改的任何历史中的点

    3. 第二代:集中式版本控制系统

      ​ 1986 年开发的并发版本系统(Concurrent Versions System,CVS)是,CVS使用Copy-Modify-Merge(拷贝、修改、合并)变化表支持对文件的同时访问和修改。它明确地将源文件的存储和用户的工作空间独立开来,并使其并行操作,这意味着可以多人同时处理文件

      ​ 但有一个明显的限制,用户必须在允许提交之前将当前修订合并到他们的工作中,这意味着一个缺点,如果服务器宕机了或者未连上服务器,开发者就不能对项目进行提交了

      ​ 第二代版本控制系统主要有 CVS、SourceSafe、Subversion、Team Foundation Server、SVK

    4. 第三代:分布式版本控制系统

      ​ 以git为首的第三代分布式VCS,解决了上述问题,每个使用者电脑上就有一个完整的数据仓库,没有网络依然可以使用,在网络具备时,再和服务器进行同步即可

      ​ 第三代版本控制系统主要有 Git、Bazaar、Mercurial、BitKeeper、Monotone

    ​ 在程序开发中,使用git的好处有

    • 开发中出现错误很常见,使用版本控制不仅可以了解引入错误的时间和地点,而且还可以用于立即恢复到正确的版本
    • 离线工作,如果git服务器出现问题,也可以在本地进行切换分支的操作,等联网后再提交、合并等操作
    • 分支管理,分支之间的互不影响这种特性可以增加团队合作的效率,也方便不同版本的开发

    一、git常用命令

    1.1 新建代码库

    #在当前目录新建一个Git代码库
    git init 
    
    #新建一个目录,将其初始化为Git代码库
    git init [project-name]
    
    # 下载一个项目和它的整个代码历史
    git clone [url]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.2 配置

    # 显示当前的Git配置
    git config --list
    
    # 编辑Git配置文件
    git config -e [--global]
    
    # 设置提交代码时的用户信息
    git config [--global] user.name "[name]"
    git config [--global] user.email "[email]"
    
    # 设置当前项目用户名
    git config --local user.name "name"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.3 增加/删除文件

    # 添加指定文件到暂存区
    git add [file1] [file2] ...
    
    # 添加指定目录到暂存区,包括子目录
    git add [dir]
    
    # 添加当前目录的所有文件到暂存区
    git add .
    
    # 添加每个变化前,都会要求确认
    # 对于同一个文件的多处变化,可以实现分次提交
    git add -p
    
    # 删除工作区文件,并且将这次删除放入暂存区
    git rm [file1] [file2] ...
    
    # 停止追踪指定文件,但该文件会保留在工作区
    git rm --cached [file]
    
    # 改名文件,并且将这个改名放入暂存区
    git mv [file-original] [file-renamed]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.4 代码提交

    # 提交暂存区到仓库区
    git commit -m [message]
    
    # 提交暂存区的指定文件到仓库区
    git commit [file1] [file2] ... -m [message]
    
    # 提交工作区自上次commit之后的变化,直接到仓库区
    git commit -a
    
    # 提交时显示所有diff信息
    git commit -v
    
    # 使用一次新的commit,替代上一次提交
    # 如果代码没有任何新变化,则用来改写上一次commit的提交信息
    git commit --amend -m [message]
    
    # 重做上一次commit,并包括指定文件的新变化
    git commit --amend [file1] [file2] ...
    
    # 撤销提交
    git reset --soft HEAD^
    
    # 将上一次提交的用户名和邮箱修改
    git commit --amend --author="name <email>"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1.5 分支

    # 列出所有本地分支
    git branch
    
    # 列出所有远程分支
    git branch -r
    
    # 列出所有本地分支和远程分支
    git branch -a
    
    # 新建一个分支,但依然停留在当前分支
    git branch [branch-name]
    
    # 新建一个分支,并切换到该分支
    git checkout -b [branch]
    
    # 新建一个分支,指向指定commit
    git branch [branch] [commit]
    
    # 新建一个分支,与指定的远程分支建立追踪关系
    git branch --track [branch] [remote-branch]
    
    # 切换到指定分支,并更新工作区
    git checkout [branch-name]
    
    # 切换到上一个分支
    git checkout -
    
    # 建立追踪关系,在现有分支与指定的远程分支之间
    git branch --set-upstream [branch] [remote-branch]
    
    # 合并指定分支到当前分支
    git merge [branch]
    
    # 选择一个commit,合并进当前分支
    git cherry-pick [commit]
    
    # 删除分支
    git branch -d [branch-name]
    
    # 删除远程分支
    git push origin --delete [branch-name]
    git branch -dr [remote/branch]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    1.6 标签

    # 列出所有tag
    git tag
    
    # 新建一个tag在当前commit
    git tag [tag]
    
    # 新建一个tag在指定commit
    git tag [tag] [commit]
    
    # 删除本地tag
    git tag -d [tag]
    
    # 删除远程tag
    git push origin :refs/tags/[tagName]
    
    # 查看tag信息
    git show [tag]
    
    # 提交指定tag
    git push [remote] [tag]
    
    # 提交所有tag
    git push [remote] --tags
    
    # 新建一个分支,指向某个tag
    git checkout -b [branch] [tag]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    1.7 查看信息

    # 显示有变更的文件
    git status
    
    # 显示当前分支的版本历史
    git log
    
    # 显示commit历史,以及每次commit发生变更的文件
    git log --stat
    
    # 每个提交在一行内显示
    git log --oneline
    
    # 格式化输出log
    # 例子git log --pretty=format:"%h - %an, %ar : %s"
    # %h:commit缩略哈希
    # %an:用户名
    # %ar:用户从近到远相对时间
    # %s:commit信息
    # 详细文档可以见:https://git-scm.com/docs/git-log
    git log --pretty=format:"<format-string>"
    
    # 显示两个commit之间的所有commit
    git log <commit1>...<commit2>
    
    # 显示某个文件的版本历史,包括文件改名
    git log --follow [file]
    git whatchanged [file]
    
    # 显示指定文件相关的每一次diff
    git log -p [file]
    
    # 显示过去5次commit
    git log -5 --pretty --oneline
    
    # 显示所有提交过的用户,按提交次数排序
    git shortlog -sn
    
    # 显示指定文件是什么人在什么时间修改过
    git blame [file]
    
    # 显示暂存区和工作区的差异
    git diff
    
    # 显示暂存区和上一个commit的差异
    git diff --cached [file]
    
    # 显示工作区与当前分支最新commit之间的差异
    git diff HEAD
    
    # 显示两次提交之间的差异
    git diff [commit1]...[commit2]
    
    # 显示两个分支之间的差异
    git diff [master]..[my-branch]
    
    # 显示两次提交之间的改动文件
    git diff --numstat [commit1]...[commit2]
    
    # 显示今天你写了多少行代码
    git diff --shortstat "@{0 day ago}"
    
    # 显示某次提交的元数据和内容变化
    git show [commit]
    
    # 显示某次提交发生变化的文件
    git show --name-only [commit]
    
    # 显示某次提交时,某个文件的内容
    git show [commit]:[filename]
    
    # git reflog可以查看删除和reset的commit信息(git log查不到)
    # git reglog包含所有分支信息,gitlog当前分支信息
    git reflog
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    1.8 远程操作

    # 下载远程仓库的所有变动
    git fetch [remote]
    
    # 显示所有远程仓库
    git remote -v
    
    # 显示某个远程仓库的信息
    git remote show [remote]
    
    # 增加一个新的远程仓库,并命名
    git remote add [shortname] [url]
    
    # 取回远程仓库的变化,并与本地分支合并
    git pull [remote] [branch]
    
    # 上传本地指定分支到远程仓库
    git push [remote] [branch]
    
    # 强行推送当前分支到远程仓库,即使有冲突
    git push [remote] --force
    
    # 推送所有分支到远程仓库
    git push [remote] --all
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1.9 撤销

    # 恢复暂存区的指定文件到工作区
    git checkout [file]
    
    # 恢复某个commit的指定文件到暂存区和工作区
    git checkout [commit] [file]
    
    # 恢复暂存区的所有文件到工作区
    git checkout .
    
    # 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
    git reset [file]
    
    # 重置暂存区与工作区,与上一次commit保持一致
    git reset --hard
    
    # 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
    git reset [commit]
    
    # 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
    git reset --hard [commit]
    
    # 重置当前HEAD为指定commit,但保持暂存区和工作区不变
    git reset --keep [commit]
    
    # 新建一个commit,用来撤销指定commit
    # 后者的所有变化都将被前者抵消,并且应用到当前分支
    git revert [commit]
    
    # 暂时将未提交的变化移除,稍后再移入
    git stash
    git stash pop
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    1.10 变基解决冲突

    Git 冲突是指在合并分支时,git不清楚两个分支都修改的文件哪个版本是正确的,这在多人合作时经常会出现。

    GIT会在文件的冲突位置添加以下信息

    <<<<<<<<<<
    ==========
    >>>>>>>>>>
    
    • 1
    • 2
    • 3

    本地版本和他人版本冲突内容通过======分隔开来,这需要选择正确的版本,来告诉git这才是对的

    合并分支一般使用git mergegit rebase,在实际开发中为了时间线保持一条直线,使用git rebase比较多

    (feature1)$ git rebase master
    
    • 1

    执行以上操作, git会执行以下操作

    1. git 会把 feature1 分支里面的每个 commit 取消掉
    2. 把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下
    3. 把 feature1 分支更新到最新的 master 分支
    4. 把上面保存的 patch 文件应用到 feature1 分支上

    在 rebase 的过程中,也许会出现冲突 conflict。在这种情况,git 会停止 rebase 并会让你去解决冲突。在解决完冲突后,用 git add 命令去更新这些内容, git commit --amend不修改commit信息继续提交,然后使用 git rebase --continue继续rebase直到合并完成

    # 继续下个冲突解决
    git rebase --continue
    
    # 取消本次rebase
    git rebase --abort
    
    • 1
    • 2
    • 3
    • 4
    • 5

    rebase变基,还可以用来修改历史commit信息,合并commit等操作

    # 修改刚提交的3个commit
    git rebase -i HEAD~3
    
    • 1
    • 2

    会开启新窗口,将pick根据需求修改为自己想要的

    pick 242f87598 commit-msg1
    pick f7656a723 commit-msg2
    pick 11f2d0297 commit-msg3
    
    • 1
    • 2
    • 3
    • p,pick:使用该次提交
    • r,reword:使用该次提交,但重新编辑commit信息
    • e,edit:使用该次提交,但停止到该次提交
    • s,squash:将该commit和前一个commit合并
    • f,fixup:将该commit和前一个commit合并,但不保留该提交的commit信息
    • x,exec:执行shell命令
    • d,drop:丢弃该commit

    解决冲突时使用e,修改历史commit信息时使用r,合并commit使用s

    二、git 源码探秘

    2.1 初始源码

    git的第一个commit

    git 初始版本命令git当前版本命令含义
    init-dbgit init初始化git仓库
    update-cachegit add添加文件到暂存区
    write-treegit write-tree使用临时索引中的内容将树对象写入Git仓库
    commit-treegit commit基于指定的树在Git仓库中创建新的commit对象
    read-treegit read-tree显示Git仓库中树对象内容
    show-diffgit diff显示暂存区与工作区之间的差异
    cat-filegit cat-file显示存储在Git仓库中的对象内容

    具体命令作用,可以查看这篇博文:源码解析:Git的第一个提交是什么样的?

    2.2 编译v1.3.0

    最近的git版本为v2.37,git自2005年至今,不断迭代,功能不断完善、也增加好多代码,阅读最新版的源码很困难,所以选择一个简单版本来阅读源码,最好可以编译,更利于理解代码运行

    在github找到git 第一个发布版本是v0.99,但是有个文件始终编译不过去,猜测是openssl版本问题,但git并没有说明对应版本,最后找到v1.3这个版本编译成功

     cd git-1.3.0
     sudo apt-get install libcurl4-openssl-dev
     sudo apt-get install expat-devel
     sudo apt-get install libexpat1-dev
     make
     make install
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    根据git-1.3.0\Documentation\tutorial.txt 可以练习这个版本的命令

    mkdir git-tutorial
    cd git-tutorial
    git-init-db
    
    echo "Hello World" >hello
    echo "Silly example" >example
    
    git add .
    git commit
    
    git branch expr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    不能commit,报错fatal: empty ident,需要更新用户

    git-repo-config "user.name" "aabond"
    
    • 1

    2.3 源码阅读

    2.3.1 git add

    发现这个版本的git add实际执行的是个shell脚本git-add.sh, 最终调用git-update-index

    git-update-index --add $verbose -z --stdin ;;
    
    • 1

    git-update-index的源码在update-index.c, 将文件遍历,根据SHA1算法写入.git/objects,并添加到缓存

    int main(int argc, const char **argv)
    {
        ...
        entries = read_cache();
        ...
        if (read_from_stdin) {
    		struct strbuf buf;
    		strbuf_init(&buf);
    		while (1) {
    			char *path_name;
    			read_line(&buf, stdin, line_termination);
    			if (buf.eof)
    				break;
    			if (line_termination && buf.buf[0] == '"')
    				path_name = unquote_c_style(buf.buf, NULL);
    			else
    				path_name = buf.buf;
    			update_one(path_name, prefix, prefix_length);
    			if (path_name != buf.buf)
    				free(path_name);
    		}
    	}
        ...
    }
    
    static void update_one(const char *path, const char *prefix, int prefix_length)
    {
    	...
    	if (add_file_to_cache(p))
    		die("Unable to process file %s", path);
    	...
    }
    
    static int add_file_to_cache(const char *path)
    {
        ...
        // 写入objects
        if (index_path(ce->sha1, path, &st, !info_only))
    		return -1;
    	...
        // 添加缓存
    	if (add_cache_entry(ce, option))
    		return error("%s: cannot add to the index - missing --add option?",
    			     path);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    写入.git/objects: 通过zlib对文件压缩,计算sha1

    int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
    {
        int size;
    	unsigned char *compressed;
    	z_stream stream;
    	unsigned char sha1[20];
    	char *filename;
    	static char tmpfile[PATH_MAX];
    	unsigned char hdr[50];
    	int fd, hdrlen;
    
    	/* Normally if we have it in the pack then we do not bother writing
    	 * it out into .git/objects/??/?{38} file.
    	 */
    	filename = write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    根据不同的对象类型type生成header

    char *write_sha1_file_prepare(void *buf,
    			      unsigned long len,
    			      const char *type,
    			      unsigned char *sha1,
    			      unsigned char *hdr,
    			      int *hdrlen)
    {
    	SHA_CTX c;
    
    	/* Generate the header */
    	*hdrlen = sprintf((char *)hdr, "%s %lu", type, len)+1;
    
    	/* Sha1.. */
    	SHA1_Init(&c);
    	SHA1_Update(&c, hdr, *hdrlen);
    	SHA1_Update(&c, buf, len);
    	SHA1_Final(sha1, &c);
    
    	return sha1_file_name(sha1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    通过sha1_file_name这个函数,可以看到将sha1的头两个字符用/分隔开来,得到文件的路径和名称

    char *sha1_file_name(const unsigned char *sha1)
    {
    	static char *name, *base;
    
    	if (!base) {
    		const char *sha1_file_directory = get_object_directory();
    		int len = strlen(sha1_file_directory);
    		base = xmalloc(len + 60);
    		memcpy(base, sha1_file_directory, len);
    		memset(base+len, 0, 60);
    		base[len] = '/';
    		base[len+3] = '/';
    		name = base + len + 1;
    	}
    	fill_sha1_path(name, sha1);
    	return base;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    缓存实际存储在.git/index

    int read_cache(void)
    {
    	int fd, i;
        ...
    	fd = open(get_index_file(), O_RDONLY);
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    char *get_index_file(void)
    {
    	if (!git_index_file)
    		setup_git_env();
    	return git_index_file;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    static void setup_git_env(void)
    {
        ...
    	git_index_file = getenv(INDEX_ENVIRONMENT);
    	if (!git_index_file) {
    		git_index_file = xmalloc(strlen(git_dir) + 7);
    		sprintf(git_index_file, "%s/index", git_dir);
    	}
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.3.2 git commit

    这个版本的git commit实际执行也是个shell脚本git-commit.sh,会调用git-commit-tree,源码位于commit-tree.c

    int main(int argc, char **argv)
    {
    	int i;
    	int parents = 0;
    	unsigned char tree_sha1[20];
    	unsigned char commit_sha1[20];
    	char comment[1000];
    	char *buffer;
    	unsigned int size;
    	
        // 设置用户邮箱 name + '@' + hostname [+ '.' + domainname
    	setup_ident();
    	setup_git_directory();
    
    	...
    
    	/* Person/date information */
    	add_buffer(&buffer, &size, "author %s\n", git_author_info(1));
    	add_buffer(&buffer, &size, "committer %s\n\n", git_committer_info(1));
    
    	/* And add the comment */
    	while (fgets(comment, sizeof(comment), stdin) != NULL)
    		add_buffer(&buffer, &size, "%s", comment);
    	
    	if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) {
    		printf("%s\n", sha1_to_hex(commit_sha1));
    		return 0;
    	}
    	else
    		return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.3.3 git branch

    同上,这个版本的git branch也是通过调用sh脚本git-branch.sh来实现,下述是创建分支相关源码

    ...
    branchname="$1"
    # 验证参数
    rev=$(git-rev-parse --verify "$head") || exit
    # 检测分支名字合法
    git-check-ref-format "heads/$branchname"
    ...
    # 创建.git/refs/heads/$branchname文件,将当前分支的Head指向的commit对象的hash写入文件中
    git update-ref "refs/heads/$branchname" $rev
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三、IDEA中使用git

    3.1 推荐插件

    • .ignore

      使用.ignore插件生成忽略文件

      使用方法:在项目名字上右键,点击New->.ignore file->.ignore file(git),然后选择对应的编程语言、框架

      idea-gitignore-new-file

    • GitToolBox
      最直观的感受是能够直接查看每行是谁commit的,还有能够自动fetch remote

    3.2 提交

    使用快捷键ctrl+k快速commit

    ctrl+k show commit dialog

    3.3 解决冲突

    当完成把本地分支完成的任务推送到gitlab时,有时会发现有冲突,提示

    there are merge conflicts.

    这种情况下,需要在本地解决冲突,再推送到gitlab

    点击git rebase, 会提示冲突文件

    idea git rebase t resolve conflict

    点击冲突文件,显示冲突部分。开始修改,可选择魔棒工具, x和>>来选择合适部分

    conflict side by side

    最后完成

    resolve ok

    最后将rebase的commit提交,pull到gitlab, 最终显示

    push gitlab ok

    参考

    1. 版本控制系统 之一 概念、分类、常见版本控制系统(CVS、SVN、BitKeeper、Git 等)
    2. https://git-scm.com/book/zh/v2
  • 相关阅读:
    C++ 字符串的 拼接,插入,查找与截取。
    斯坦福计算机视觉cs231n-assignment3总结
    Python | Leetcode Python题解之第200题岛屿数量
    Redis内存兜底策略——内存淘汰及回收机制
    在VSCode上画UML的三个插件
    chrome浏览器也能做自动化测试
    python四舍五入(round精度不够,有时不能实现四舍五入)
    App前端开发跨平台框架比较:React Native、Flutter、Xamarin等
    光耦合器继电器与传统继电器:哪种最适合您的项目?
    四种静态查找方法(c代码解析)
  • 原文地址:https://blog.csdn.net/qq_23091073/article/details/125542564