Git命令

基本操作

C

  • 添加到版本控制: git add filename.md
  • 添加描述并提交: git commit -m "first init"

R

  • 查看图形 log:git log --graph
  • 查看单行 log:git log --pretty=oneline
  • 查看分支合并情况:git log --graph --pretty=oneline --abbrev-commit
  • 查看工作区和版本库最新版本的区别: git diff HEAD -- test.xml
  • 列出每个人的最近 commit 记录:git shortlog
  • 列出每个人的 commit 次数:git shortlog -sne

U

  • 修改提交信息:git commit --amend
  • 取消添加(git add .)的命令:git reset HEAD test.xml

D

  • 移除工作空间的文件: git rm test.xml
  • 丢弃工作区的修改: git checkout -- test.xml,其中--很重要,没有这个--,就变成了“切换到另一个分支”。

RESET

How can I undo those commits from the local repository?
https://stackoverflow.com/questions/927358/how-do-i-undo-the-most-recent-local-commits-in-git

1
git reset HEAD~

远程仓库

分支

创建空白分支

1
git checkout --orphan gh-pages

delete a git branch both locally and remotely.

1
2
3
$ git push --delete <remote_name> <branch_name>
$ git branch -d <branch_name>
# (https://stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-both-locally-and-remotely)

本地分支

  • 删除本地分支:git branch -d {the_local_branch}

远程分支

合并 merge & rebase

1
2
3
4
5
6
7
8
9
10
11
12
13
# 我要新增一個功能,所以在master上開一個branch叫feature1。
# 我在新增功能時,加了3個commit在feature1中。
# 當功能開發完成時,checkout回master,要將 feature1合併回master,這時可以有兩個選擇:

# 选择1:master會將feature1上的3個commit全視為master的commit,並將HEAD移到跟feature1的HEAD相同的commit上。
git merge --ff feature1

# 选择2:master會新增一個commit,內容為feature1上的改變,並將HEAD移到新的commit上。
git merge --no-ff feature1

# 至於該使用哪種方式,就要看個人需求了,如果是新增功能的branch,個人認為使用non-fast-forward的方式比較好。
# 這樣可以很清楚的看出哪些是新增功能用的commit,哪些是原本master的commit。master才不會有太多不相干的commit交錯在一起。
# 而且master要移除功能時,也只要處理一個commit就可以了。

remove untracked files

1
2
$ git clean -d -f
$ git clean --help

branch - How to remove local (untracked) files from the current Git working tree? - Stack Overflow

git stash

  • Git 还提供了一个 stash 功能,可以把当前工作状态“储藏”起来,等以后恢复现场后继续工作。

  • 查看 stash 的列表:git stash list

  • 恢复:git stash pop 或者 git stash apply

    一是用 git stash apply 恢复,但是恢复后,stash 内容并不删除,你需要用 git stash drop 来删除;
    另一种方式是用 git stash pop,恢复的同时把 stash 内容也删了:

  • 参考资料:
    Bug 分支

添加注释的技巧

参考golang.org/x/net的提交日志:

1
2
3
4
5
6
7
8
9
10
* 92b859f - ipv6: update icmp parameters (4 days ago) <Mikio Hara>
* 344b2e3 - ipv4: update icmp parameters (4 days ago) <Mikio Hara>
* 08b7f81 - internal/iana: add address family number constants (4 days ago) <Mikio Hara>
* a4c73ec - icmp: use subtests (4 days ago) <Mikio Hara>
* 24dd378 - dns/dnsmessage: reject compressed SRV resource records (6 days ago) <Ian Gudger>
* e0c57d8 - CONTRIBUTING.md: remove note about not accepting Pull Requests (9 days ago) <Andrew Bonventre>
* 892bf7b - dns/dnsmessage: correctly handle multiple and >255 byte TXT records (10 days ago) <Ian Gudger>
* 803fdb9 - ipv4, ipv6, icmp, internal/socket: fix build on netbsd/arm (10 days ago) <Mikio Hara>
* ae89d30 - route: avoid unnecessary type conversions (12 days ago) <namusyaka>
* d0aafc7 - trace: fix races on concurrent Trace method calls (2 weeks ago) <David Howden>

名称最好简洁、准确,
可以总结:模块: 动词 + 名词;
动词:

1
2
3
4
5
6
7
8
9
- add
- update
- fix
- remove
- simply
- enable
- disable
- rename

delete all tags

To delete remote tags (before deleting local tags) simply do:

1
git tag -l | grep v2 -v | xargs -n 1 git push --delete origin

and then delete the local copies:

1
git tag -l | grep v2 -v | xargs git tag -d

How do you rename a Git tag?

1
2
3
4
5
6
git tag new old
git tag -d old
git push origin :refs/tags/old
git push --tags

git pull --prune --tags # Finally, make sure that the other users remove the deleted tag. Please tell them (co-workers) to run the following command

Remove a file from a Git repository without deleting it from the local filesystem

1
2
3
4
5
# For single file:
git rm --cached mylogfile.log

# For single directory:
git rm --cached -r mydirectory

git delete remotes: remote refs do not exist - Stack Overflow

1
2
3
4
# You may need to prune your local "cache" of remote branches first. Try running:
git fetch -p origin
# or
git fetch --prune origin

How to revert multiple git commits? - Stack Overflow

Expanding what I wrote in a comment

The general rule is that you should not rewrite (change) history that you have published, because somebody might have based their work on it. If you rewrite (change) history, you would make problems with merging their changes and with updating for them.

So the solution is to create a new commit which reverts changes that you want to get rid of. You can do this using git revert command.

You have the following situation:

1
A <-- B  <-- C <-- D                                  <-- master <-- HEAD

(arrows here refers to the direction of the pointer: the “parent” reference in the case of commits, the top commit in the case of branch head (branch ref), and the name of branch in the case of HEAD reference).

What you need to create is the following:

1
A <-- B  <-- C <-- D <-- [(BCD)-1]                   <-- master <-- HEAD

where [(BCD)^-1] means the commit that reverts changes in commits B, C, D. Mathematics tells us that (BCD)-1 = D-1 C-1 B-1, so you can get the required situation using the following commands:

1
2
3
4
$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message for all of them"

Works for everything except merge commits.


Alternate solution would be to checkout contents of commit A, and commit this state. Also works with merge commits. Added files will not be deleted, however. If you have any local changes git stash them first:

1
2
$ git checkout -f A -- . # checkout that revision over the top of local files
$ git commit -a

Then you would have the following situation:

1
A <-- B  <-- C <-- D <-- A'                       <-- master <-- HEAD

The commit A’ has the same contents as commit A, but is a different commit (commit message, parents, commit date).


Alternate solution by Jeff Ferland, modified by Charles Bailey builds upon the same idea, but uses git reset. Here it is slightly modified, this way WORKS FOR EVERYTHING:

1
2
3
$ git reset --hard A
$ git reset --soft D # (or ORIG_HEAD or @{1} [previous location of HEAD]), all of which are D
$ git commit

统计

统计 tag1 和 tag2 之间的行数

1
2
3
4
git log tag1..tag2 --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 + $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }'

# 或者
git log tag1..tag2 --pretty=tformat: --numstat | awk '{ add += $1 ; subs += $2 ; loc += $1 + $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }'

统计某个人修改的代码行数

1
2
3
4
5
# 当前作者
git log --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 + $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }'

# 或者修改 username 为此人名字
git log --author="username" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -

按个人分别统计

1
git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done

统计总行数

1
git log  --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -

按时间范围统计个人行数

1
2
git log --since="2020-01-08" --before="2021-11-14" --author="lilou" \
--pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "新增行数: %s, 移除行数: %s, 总行数: %s\n", add, subs, loc }'

仓库提交排名

1
git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5

贡献者统计

1
git log --pretty='%aN' | sort -u | wc -l

提交数统计

1
git log --oneline | wc -l

参考链接