四时宝库

程序员的知识宝库

Git之旅② - 子命令与设计哲学(git ci命令)

如果有人让你猜Git总共有多少命令,你可能会在思索一番之后说:可能几十个吧(虽然常用的不超过10个)。那么,当我告诉你大概有170多个时,你会是什么反应呢?

1. 所有Git子命令

Git版本号(这里所用版本号:2.22.0)

~ git version

git version 2.22.0

查看子命令的几种方法

1.1 通过帮助信息

git help

git help -a // (显示可用的子命令)

git help -g // (最常用的 Git 向导)

1.2 直接查看子命令

//Linux

ls -lF /usr/local/git/libexec/git-core | wc -l

//mac

ls -lF /usr/local/Cellar/git/2.22.0_1/libexec/git-core |wc -l

目前,2.22.0版本子命令数:170多个

2. Git子命令

下图中是常用的Git命令

2.1 几个Git底层命令

  • 保存数据并生成sha-1字符
  • echo xxx | git hash-object -w --stdin
  • git hash-object filename
  • 查看对象类型
  • git cat-file -t sha1
  • 查看对象内容
  • git cat-file -p sha-1
  • 显示暂存区目录树,第三个字段是暂存区编号
  • git ls-files -s(--stage)
  • 查看HEAD指向的目录树,-l参数显示文件大小
  • git ls-tree -l -r -t HEAD
  • 查看打包的对象文件
  • git verify-pack -v .git/objects/pack/pack-xxxxxxxxxxxx.idx
  • 数据对象打包
  • git gc
  • 文件索引添加到暂存区
  • git update-index --add --cacheinfo 100644 9f358a4addefcab294b83e4282bfef1f9625a249b
  • 生成一棵树(tree对象)
  • git write-tree
  • 生成commit对象,并指向一棵树
  • echo "second commit " | git commit-tree 43e82 -p c760d78
  • 查看当前所有分支指向的版本号
  • git show-ref

2.2 剖析Git的3把刷子与1枚军刀

3把刷子

ls //查看当前工作区

ls-files //查看当前暂存区

ls-tree //查看当前仓库区

1枚瑞士军刀

cat-file //查看git对象

3. 为什么这么多命令?

这要从两方面分析:

  1. 设计哲学
  2. 源码

3.1 设计哲学角度

linux

一切皆文件

单一目的的小程序

组合小程序完成复杂任务

文本文件保存配置信息

......

git

还是文件 (对象)

命令 (小程序)

.gitconfig .gitignore (配置信息)

......

Linux kernel与Git都来自同一人之手,所以你懂的...

3.2 源码角度

static struct cmd_struct commands[] = {
	{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
	{ "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
	{ "annotate", cmd_annotate, RUN_SETUP | NO_PARSEOPT },
	{ "apply", cmd_apply, RUN_SETUP_GENTLY },
	{ "archive", cmd_archive, RUN_SETUP_GENTLY },
	{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
	{ "blame", cmd_blame, RUN_SETUP },
	{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
	{ "bundle", cmd_bundle, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "cat-file", cmd_cat_file, RUN_SETUP },
	{ "check-attr", cmd_check_attr, RUN_SETUP },
	{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
	{ "check-mailmap", cmd_check_mailmap, RUN_SETUP },
	{ "check-ref-format", cmd_check_ref_format, NO_PARSEOPT  },
	{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
	{ "checkout-index", cmd_checkout_index,RUN_SETUP | NEED_WORK_TREE},
	{ "cherry", cmd_cherry, RUN_SETUP },
	{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
	{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
	{ "clone", cmd_clone },
	{ "column", cmd_column, RUN_SETUP_GENTLY },
	{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
	{ "commit-graph", cmd_commit_graph, RUN_SETUP },
	{ "commit-tree", cmd_commit_tree, RUN_SETUP | NO_PARSEOPT },
	{ "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
	{ "count-objects", cmd_count_objects, RUN_SETUP },
	{ "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "describe", cmd_describe, RUN_SETUP },
	{ "diff", cmd_diff, NO_PARSEOPT },
	{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
	{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
	{ "difftool", cmd_difftool, RUN_SETUP_GENTLY },
	{ "env--helper", cmd_env__helper },
	{ "fast-export", cmd_fast_export, RUN_SETUP },
	{ "fetch", cmd_fetch, RUN_SETUP },
	{ "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
	{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
	{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
	{ "format-patch", cmd_format_patch, RUN_SETUP },
	{ "fsck", cmd_fsck, RUN_SETUP },
	{ "fsck-objects", cmd_fsck, RUN_SETUP },
	{ "gc", cmd_gc, RUN_SETUP },
	{ "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
	{ "grep", cmd_grep, RUN_SETUP_GENTLY },
	{ "hash-object", cmd_hash_object },
	{ "help", cmd_help },
	{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "init", cmd_init_db },
	{ "init-db", cmd_init_db },
	{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
	{ "log", cmd_log, RUN_SETUP },
	{ "ls-files", cmd_ls_files, RUN_SETUP },
	{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
	{ "ls-tree", cmd_ls_tree, RUN_SETUP },
	{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "mailsplit", cmd_mailsplit, NO_PARSEOPT },
	{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
	{ "merge-base", cmd_merge_base, RUN_SETUP },
	{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
	{ "merge-index", cmd_merge_index, RUN_SETUP | NO_PARSEOPT },
	{ "merge-ours", cmd_merge_ours, RUN_SETUP | NO_PARSEOPT },
	{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },
	{ "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT },
	{ "mktree", cmd_mktree, RUN_SETUP },
	{ "multi-pack-index", cmd_multi_pack_index, RUN_SETUP_GENTLY },
	{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
	{ "name-rev", cmd_name_rev, RUN_SETUP },
	{ "notes", cmd_notes, RUN_SETUP },
	{ "pack-objects", cmd_pack_objects, RUN_SETUP },
	{ "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
	{ "pack-refs", cmd_pack_refs, RUN_SETUP },
	{ "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "pickaxe", cmd_blame, RUN_SETUP },
	{ "prune", cmd_prune, RUN_SETUP },
	{ "prune-packed", cmd_prune_packed, RUN_SETUP },
	{ "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
	{ "push", cmd_push, RUN_SETUP },
	{ "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
	{ "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
	{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
	{ "rebase--interactive", cmd_rebase__interactive, RUN_SETUP | NEED_WORK_TREE },
	{ "receive-pack", cmd_receive_pack },
	{ "reflog", cmd_reflog, RUN_SETUP },
	{ "remote", cmd_remote, RUN_SETUP },
	{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
	{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
	{ "repack", cmd_repack, RUN_SETUP },
	{ "replace", cmd_replace, RUN_SETUP },
	{ "rerere", cmd_rerere, RUN_SETUP },
	{ "reset", cmd_reset, RUN_SETUP },
	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
	{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
	{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
	{ "rm", cmd_rm, RUN_SETUP },
	{ "send-pack", cmd_send_pack, RUN_SETUP },
	{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
	{ "show", cmd_show, RUN_SETUP },
	{ "show-branch", cmd_show_branch, RUN_SETUP },
	{ "show-index", cmd_show_index },
	{ "show-ref", cmd_show_ref, RUN_SETUP },
	{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
	/*
	 * NEEDSWORK: Until the builtin stash is thoroughly robust and no
	 * longer needs redirection to the stash shell script this is kept as
	 * is, then should be changed to RUN_SETUP | NEED_WORK_TREE
	 */
	{ "stash", cmd_stash },
	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
	{ "stripspace", cmd_stripspace },
	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
	{ "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_PARSEOPT },
	{ "update-index", cmd_update_index, RUN_SETUP },
	{ "update-ref", cmd_update_ref, RUN_SETUP },
	{ "update-server-info", cmd_update_server_info, RUN_SETUP },
	{ "upload-archive", cmd_upload_archive, NO_PARSEOPT },
	{ "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
	{ "upload-pack", cmd_upload_pack },
	{ "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "verify-commit", cmd_verify_commit, RUN_SETUP },
	{ "verify-pack", cmd_verify_pack },
	{ "verify-tag", cmd_verify_tag, RUN_SETUP },
	{ "version", cmd_version },
	{ "whatchanged", cmd_whatchanged, RUN_SETUP },
	{ "worktree", cmd_worktree, RUN_SETUP | NO_PARSEOPT },
	{ "write-tree", cmd_write_tree, RUN_SETUP },
};

....

int cmd_main(int argc, const char **argv)
{
	const char *cmd;
	int done_help = 0;

	cmd = argv[0];
	if (!cmd)
		cmd = "git-help";
	else {
		const char *slash = find_last_dir_sep(cmd);
		if (slash)
			cmd = slash + 1;
	}

	trace_command_performance(argv);

	/*
	 * "git-xxxx" is the same as "git xxxx", but we obviously:
	 *
	 *  - cannot take flags in between the "git" and the "xxxx".
	 *  - cannot execute it externally (since it would just do
	 *    the same thing over again)
	 *
	 * So we just directly call the builtin handler, and die if
	 * that one cannot handle it.
	 */
	if (skip_prefix(cmd, "git-", &cmd)) {
		argv[0] = cmd;
		handle_builtin(argc, argv);
		die(_("cannot handle %s as a builtin"), cmd);
	}
       ....

可以看出来,但凡带 "git-" 前缀的命令与去掉前缀作为git的子命令是一样的。

反应在代码中,每一个命令又是一个单独的文件存在。

如此以来,复杂的git就化整为零,靠着众多子命令单独的功能,实现一个复杂的版本控制系统。

看来,好的程序员必修的一门课,放在第一位的绝对是:哲学。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接