From 3f4e375e25a117c2cf5c2f571714a25b6b772163 Mon Sep 17 00:00:00 2001 From: zpl1025 Date: Wed, 11 Mar 2015 18:10:09 +0800 Subject: [PATCH] [translated] 20150211 25 Tips for Intermediate Git Users.md --- ...0211 25 Tips for Intermediate Git Users.md | 470 ------------------ ...0211 25 Tips for Intermediate Git Users.md | 469 +++++++++++++++++ 2 files changed, 469 insertions(+), 470 deletions(-) delete mode 100644 sources/tech/20150211 25 Tips for Intermediate Git Users.md create mode 100644 translated/tech/20150211 25 Tips for Intermediate Git Users.md diff --git a/sources/tech/20150211 25 Tips for Intermediate Git Users.md b/sources/tech/20150211 25 Tips for Intermediate Git Users.md deleted file mode 100644 index 71ead3ad6a..0000000000 --- a/sources/tech/20150211 25 Tips for Intermediate Git Users.md +++ /dev/null @@ -1,470 +0,0 @@ -zpl1025 -25 Tips for Intermediate Git Users -================================================================================ -I’ve been using git for about 18 months now and thought I knew it pretty well. Then we had [Scott Chacon][1] from GitHub over to do some training at [LVS, a supplier/developer of betting/gaming software][2] (where contracted until 2013) and I learnt a ton in the first day. - -As someone who’s always felt fairly comfortable in Git, I thought sharing some of the nuggets I learnt with the community might help someone to find an answer without needing to do lots of research. - -### Basic Tips ### - -#### 1. First Steps After Install #### - -After installing Git, the first thing you should do is configure your name and email, as every commit will have these details: - - $ git config --global user.name "Some One" - $ git config --global user.email "someone@gmail.com" - -#### 2. Git is Pointer-Based #### - -Everything stored in git is in a file. When you create a commit it creates a file containing your commit message and associated data (name, email, date/time, previous commit, etc) and links it to a tree file. The tree file contains a list of objects or other trees. The object or blob is the actual content associated with the commit (a file, if you will, although the filename isn’t stored in the object, but in the tree). All of these files are stored with a filename of a SHA-1 hash of the object. - -From there branches and tags are simply files containing (basically) a SHA-1 hash which points to the commit. Using these references allows for a lot of flexibility and speed, as creating a new branch is as simple as creating a file with the name of the branch and the SHA-1 reference to the commit you’re branching from. Of course, you’d never do that as you’d use the Git command line tools (or a GUI), but it’s that simple. - -You may have heard references to the HEAD. This is simply a file containing the SHA-1 reference of the commit you’re currently pointing to. If you’re resolving a merge conflict and see HEAD, that’s nothing to do with a particular branch or necessarily a particular point on the branch but where you currently are. - -All the branch pointers are kept in .git/refs/heads, HEAD is in .git/HEAD and tags are in .git/refs/tags – feel free to have a look in there. - -#### 3. Two Parents – of course! #### - -When viewing a merge commit message in a log, you will see two parents (as opposed to the normal one for a work-based commit). The first parent is the branch you were on and the second is the one you merged in to it. - -#### 4. Merge Conflicts #### - -By now I’m sure you have had a merge conflict and had to resolve it. This is normally done by editing the file, removing the <<<<, ====, >>>> markers and the keeping the code you want to store. Sometimes it’s nice to see the code before either change, i.e. before you made the change in both branches that now conflicts. This is one command away: - - $ git diff --merge - diff --cc dummy.rb - index 5175dde,0c65895..4a00477 - --- a/dummy.rb - +++ b/dummy.rb - @@@ -1,5 -1,5 +1,5 @@@ - class MyFoo - def say - - puts "Bonjour" - - puts "Hello world" - ++ puts "Annyong Haseyo" - end - end - -If the file is binary, diffing files isn’t so easy… What you’ll normally want to do is to try each version of the binary file and decide which one to use (or manually copy portions over in the binary file’s editor). To pull a copy of the file from a particular branch (say you’re merging master and feature132): - - $ git checkout master flash/foo.fla # or... - $ git checkout feature132 flash/foo.fla - $ # Then... - $ git add flash/foo.fla - -Another way is to cat the file from git – you can do this to another filename then copy the correct file over (when you’ve decided which it is) to the normal filename: - - $ git show master:flash/foo.fla > master-foo.fla - $ git show feature132:flash/foo.fla > feature132-foo.fla - $ # Check out master-foo.fla and feature132-foo.fla - $ # Let's say we decide that feature132's is correct - $ rm flash/foo.fla - $ mv feature132-foo.fla flash/foo.fla - $ rm master-foo.fla - $ git add flash/foo.fla - -UPDATE: Thanks to Carl in the comments on the original blog post for the reminder, you can actually use “git checkout —ours flash/foo.fla” and “git checkout —theirs flash/foo.fla” to checkout a particular version without remembering which branches you merge in. Personally I prefer to be more explicit, but the option is there… - -Remember to add the file after resolving the merge conflict (as I do above). - -### Servers, Branching and Tagging ### - -#### 5. Remote Servers #### - -One of the most powerful features of Git is the ability to have more than one remote server (as well as the fact that you’re running a local repository always). You don’t always need write access either, you may have multiple servers you read from (to merge work in) and then write to another. To add a new remote server is simple: - - $ git remote add john git@github.com:johnsomeone/someproject.git - -If you want to see information about your remote servers you can do: - - # shows URLs of each remote server - $ git remote -v - - # gives more details about each - $ git remote show name - -You can always see the differences between a local branch and a remote branch: - - $ git diff master..john/master - -You can also see the changes on HEAD that aren’t on that remote branch: - - $ git log remote/branch.. - # Note: no final refspec after .. - -#### 6. Tagging #### - -In Git there are two types of tag – a lightweight tag and an annotated tag. Bearing in mind Tip 2 about Git being pointer based, the difference between the two is simple. A lightweight tag is simply a named pointer to a commit. You can always change it to point to another commit. An annotated tag is a name pointer to a tag object, with it’s own message and history. As it has it’s own message it can be GPG signed if required. - -Creating the two types of tag is easy (and one command line switch different) - - $ git tag to-be-tested - $ git tag -a v1.1.0 # Prompts for a tag message - -#### 7. Creating Branches #### - -Creating branches in git is very easy (and lightning quick due to it only needing to create a less than 100 byte file). The longhand way of creating a new branch and switching to it: - - $ git branch feature132 - $ git checkout feature132 - -Of course, if you know you’re going to want to switch to it straight away you can do it in one command: - - $ git checkout -b feature132 - -If you want to rename a local branch it’s as easy as (the long way to show what happens): - - $ git checkout -b twitter-experiment feature132 - $ git branch -d feature132 - -Update: Or you can (as Brian Palmer points out in the comments on the original blog post) just use the -m switch to “git branch” to do it in one step (as Mike points out, if you only specify one branch it renames your current branch): - - $ git branch -m twitter-experiment - $ git branch -m feature132 twitter-experiment - -#### 8. Merging Branches #### - -At some point in the future, you’re going to want to merge your changes back in. There are two ways to do this: - - $ git checkout master - $ git merge feature83 # Or... - $ git rebase feature83 - -The difference between merge and rebase is that merge tries to resolve the changes and create a new commit that blends them. Rebase tries to take your changes since you last diverged from the other branch and replay them from the HEAD of the other branch. However, don’t rebase after you’ve pushed a branch to a remote server – this can cause confusion/problems. - -If you aren’t sure which branches still have unique work on them – so you know which you need to merge and which ones can be removed, there are two switches to git branch that help: - - # Shows branches that are all merged in to your current branch - $ git branch --merged - - # Shows branches that are not merged in to your current branch - $ git branch --no-merged - -#### 9. Remote Branches #### - -If you have a local branch that you’d like to appear on a remote server, you can push it up with one command: - - $ git push origin twitter-experiment:refs/heads/twitter-experiment - # Where origin is our server name and twitter-experiment is the branch - -Update: Thanks to Erlend in the comments on the original blog post – this is actually the same as doing `git push origin twitter-experiment` but by using the full syntax you can see that you can actually use different names on both ends (so your local can be `add-ssl-support` while your remote name can be `issue-1723`). - -If you want to delete a branch from the server (note the colon before the branch name): - - $ git push origin :twitter-experiment - -If you want to show the state of all remote branches you can view them like this: - - $ git remote show origin - -This may list some branches that used to exist on the server but now don’t exist. If this is the case you can easily remove them from your local checkout using: - - $ git remote prune - -Finally, if you have a remote branch that you want to track locally, the longhand way is: - - $ git branch --track myfeature origin/myfeature - $ git checkout myfeature - -However, newer versions of Git automatically set up tracking if you use the -b flag to checkout: - - $ git checkout -b myfeature origin/myfeature - -### Storing Content in Stashes, Index and File System ### - -#### 10. Stashing #### - -In Git you can drop your current work state in to a temporary storage area stack and then re-apply it later. The simple case is as follows: - - $ git stash - # Do something... - $ git stash pop - -A lot of people recommend using `git stash apply` instead of pop, however if you do this you end up with a long list of stashes left hanging around. “pop” will only remove it from the stack if it applies cleanly. If you’ve used `git stash apply` you can remove the last item from the stack anyway using: - - $ git stash drop - -Git will automatically create a comment based on the current commit message. If you’d prefer to use a custom message (as it may have nothing to do with the previous commit): - - $ git stash save "My stash message" - -If you want to apply a particular stash from your list (not necessarily the last one) you can list them and apply it like this: - - $ git stash list - stash@{0}: On master: Changed to German - stash@{1}: On master: Language is now Italian - $ git stash apply stash@{1} - -#### 11. Adding Interactively #### - -In the subversion world you change files and then just commit everything that has changed. In Git you have a LOT more power to commit just certain files or even certain patches. To commit certain files or parts of files you need to go in to interactive mode. - - $ git add -i - staged unstaged path - - - *** Commands *** - 1: status 2: update 3: revert 4: add untracked - 5: patch 6: diff 7: quit 8: help - What now> - -This drops you in to a menu based interactive prompt. You can use the numbers of the commands or the highlighted letters (if you have colour highlighting turned on) to go in to that mode. Then it’s normally a matter of typing the numbers of the files you want to apply that action to (you can use formats like 1 or 1-4 or 2,4,7). - -If you want to go to patch mode (‘p’ or ‘5’ from interactive mode) you can also go straight in to that mode: - - $ git add -p - diff --git a/dummy.rb b/dummy.rb - index 4a00477..f856fb0 100644 - --- a/dummy.rb - +++ b/dummy.rb - @@ -1,5 +1,5 @@ - class MyFoo - def say - - puts "Annyong Haseyo" - + puts "Guten Tag" - end - end - Stage this hunk [y,n,q,a,d,/,e,?]? - -As you can see you then get a set of options at the bottom for choosing to add this changed part of the file, all changes from this file, etc. Using the ‘?’ command will explain the options. - -#### 12. Storing/Retrieving from the File System #### - -Some projects (the Git project itself for example) store additional files directly in the Git file system without them necessarily being a checked in file. - -Let’s start off by storing a random file in Git: - - $ echo "Foo" | git hash-object -w --stdin - 51fc03a9bb365fae74fd2bf66517b30bf48020cb - -At this point the object is in the database, but if you don’t set something up to point to that object it will be garbage collected. The easiest way is to tag it: - - $ git tag myfile 51fc03a9bb365fae74fd2bf66517b30bf48020cb - -Note that here we’ve used the tag myfile. When we need to retrieve the file we can do it with: - - $ git cat-file blob myfile - -This can be useful for utility files that developers may need (passwords, gpg keys, etc) but you don’t want to actually check out on to disk every time (particularly in production). - -### Logging and What Changed? ### - -#### 13. Viewing a Log #### - -You can’t use Git for long without using ‘git log’ to view your recent commits. However, there are some tips on how to use it better. For example, you can view a patch of what changed in each commit with: - - $ git log -p - -Or you can just view a summary of which files changed with: - - $ git log --stat - -There’s a nice alias you can set up which shows abbreviated commits and a nice graph of branches with the messages on a single line (like gitk, but on the command line): - - $ git config --global alias.lol "log --pretty=oneline --abbrev-commit --graph --decorate" - $ git lol - * 4d2409a (master) Oops, meant that to be in Korean - * 169b845 Hello world - -#### 14. Searching in the Log #### - -If you want to search for a particular author you can specify that: - - $ git log --author=Andy - -Update: Thanks to Johannes in the comments, I’ve cleared up some of the confusion here. - -Or if you have a search term that appears in the commit message: - - $ git log --grep="Something in the message" - -There’s also a more powerful command called the pickaxe command that look for the entry that removes or adds a particular piece of content (i.e. when it first appeared or was removed). This can tell you when a line was added (but not if a character on that line was later changed): - - $ git log -S "TODO: Check for admin status" - -What about if you changed a particular file, e.g. `lib/foo.rb` - - $ git log lib/foo.rb - -Let’s say you have a `feature/132` branch and a `feature/145` and you want to view the commits on those branches that aren’t on master (note the ^ meaning not): - - $ git log feature/132 feature/145 ^master - -You can also narrow it down to a date range using ActiveSupport style dates: - - $ git log --since=2.months.ago --until=1.day.ago - -By default it will use OR to combine the query, but you can easily change it to use AND (if you have more than one criteria) - - $ git log --since=2.months.ago --until=1.day.ago --author=andy -S "something" --all-match - -#### 15. Selecting Revisions to View/Change #### - -There are a number of items you can specify when referring to a revision, depending on what you know about it: - - $ git show 12a86bc38 # By revision - $ git show v1.0.1 # By tag - $ git show feature132 # By branch name - $ git show 12a86bc38^ # Parent of a commit - $ git show 12a86bc38~2 # Grandparent of a commit - $ git show feature132@{yesterday} # Time relative - $ git show feature132@{2.hours.ago} # Time relative - -Note that unlike the previous section, a caret on the end means the parent of that commit – a caret at the start means not on this branch. - -#### 16. Selecting a Range #### - -The easiest way is to use: - - $ git log origin/master..new - # [old]..[new] - everything you haven't pushed yet - -You can also omit the [new] and it will use your current HEAD. - -### Rewinding Time & Fixing Mistakes ### - -#### 17. Resetting changes #### - -You can easily unstage a change if you haven’t committed it using: - - $ git reset HEAD lib/foo.rb - -Often this is aliased to ‘unstage’ as it’s a bit non-obvious. - - $ git config --global alias.unstage "reset HEAD" - $ git unstage lib/foo.rb - -If you’ve committed the file already, you can do two things – if it’s the last commit you can just amend it: - - $ git commit --amend - -This undoes the last commit, puts your working copy back as it was with the changes staged and the commit message ready to edit/commit next time you commit. - -If you’ve committed more than once and just want to completely undo them, you can reset the branch back to a previous point in time. - - $ git checkout feature132 - $ git reset --hard HEAD~2 - -If you actually want to bring a branch to point to a completely different SHA1 (maybe you’re bringing the HEAD of a branch to another branch, or a further commit) you can do the following to do it the long way: - - $ git checkout FOO - $ git reset --hard SHA - -There’s actually a quicker way (as it doesn’t change your working copy back to the state of FOO first then forward to SHA): - - $ git update-ref refs/heads/FOO SHA - -#### 18. Committing to the Wrong Branch #### - -OK, let’s assume you committed to master but should have created a topic branch called experimental instead. To move those changes over, you can create a branch at your current point, rewind head and then checkout your new branch: - - $ git branch experimental # Creates a pointer to the current master state - $ git reset --hard master~3 # Moves the master branch pointer back to 3 revisions ago - $ git checkout experimental - -This can be more complex if you’ve made the changes on a branch of a branch of a branch etc. Then what you need to do is rebase the change on a branch on to somewhere else: - - $ git branch newtopic STARTPOINT - $ git rebase oldtopic --onto newtopic - -#### 19. Interactive Rebasing #### - -This is a cool feature I’ve seen demoed before but never actually understood, now it’s easy. Let’s say you’ve made 3 commits but you want to re-order them or edit them (or combine them): - - $ git rebase -i master~3 - -Then you get your editor pop open with some instructions. All you have to do is amend the instructions to pick/squash/edit (or remove them) commits and save/exit. Then after editing you can `git rebase —continue` to keep stepping through each of your instructions. - -If you choose to edit one, it will leave you in the state you were in at the time you committed that, so you need to use git commit —amend to edit it. - -**Note: DO NOT COMMIT DURING REBASE – only add then use —continue, —skip or —abort.** - -#### 20. Cleaning Up #### - -If you’ve committed some content to your branch (maybe you’ve imported an old repo from SVN) and you want to remove all occurrences of a file from the history: - - $ git filter-branch --tree-filter 'rm -f *.class' HEAD - -If you’ve already pushed to origin, but have committed the rubbish since then, you can also do this for your local system before pushing: - - $ git filter-branch --tree-filter 'rm -f *.class' origin/master..HEAD - -### Miscellaneous Tips ### - -#### 21. Previous References You’ve Viewed #### - -If you know you’ve previously viewed a SHA-1, but you’ve done some resetting/rewinding you can use the reflog commands to view the SHA-1s you’ve recently viewed: - - $ git reflog - $ git log -g # Same as above, but shows in 'log' format - -#### 22. Branch Naming #### - -A lovely little tip – don’t forget that branch names aren’t limited to a-z and 0-9. It can be quite nice to use / and . in names for fake namespacing or versionin, for example: - - $ # Generate a changelog of Release 132 - $ git shortlog release/132 ^release/131 - $ # Tag this as v1.0.1 - $ git tag v1.0.1 release/132 - -#### 23. Finding Who Dunnit #### - -Often it can be useful to find out who changed a line of code in a file. The simple command to do this is: - - $ git blame FILE - -Sometimes the change has come from a previous file (if you’ve combined two files, or you’ve moved a function) so you can use: - - $ # shows which file names the content came from - $ git blame -C FILE - -Sometimes it’s nice to track this down by clicking through changes and going further and further back. There’s a nice in-built gui for this: - - $ git gui blame FILE - -#### 24. Database Maintenance #### - -Git doesn’t generally require a lot of maintenance, it pretty much takes care of itself. However, you can view the statistics of your database using: - - $ git count-objects -v - -If this is high you can choose to garbage collect your clone. This won’t affect pushes or other people but it can make some of your commands run much faster and take less space: - - $ git gc - -It also might be worth running a consistency check every so often: - - $ git fsck --full - -You can also add a `—auto` parameter on the end (if you’re running it frequently/daily from crontab on your server) and it will only fsck if the stats show it’s necessary. - -When checking, getting “dangling” or “unreachable” is fine, this is often a result of rewinding heads or rebasing. Getting “missing” or “sha1 mismatch” is bad… Get professional help! - -#### 25. Recovering a Lost Branch #### - -If you delete a branch experimental with -D you can recreate it with: - - $ git branch experimental SHA1_OF_HASH - -You can often find the SHA1 hash using git reflog if you’ve accessed it recently. - -Another way is to use `git fsck —lost-found`. A dangling commit here is the lost HEAD (it will only be the HEAD of the deleted branch as the HEAD^ is referred to by HEAD so it’s not dangling) - -### Done! ### - -Wow, the longest blog post I’ve ever written, I hope someone finds it useful. If you did, or if you have any questions let me know in the comments… - --------------------------------------------------------------------------------- - -via: https://www.andyjeffries.co.uk/25-tips-for-intermediate-git-users/ - -作者:[Andy Jeffries][a] -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出 - -[a]:https://www.andyjeffries.co.uk/author/andy-jeffries/ -[1]:http://gitcasts.com/about -[2]:http://www.lvs.co.uk/ diff --git a/translated/tech/20150211 25 Tips for Intermediate Git Users.md b/translated/tech/20150211 25 Tips for Intermediate Git Users.md new file mode 100644 index 0000000000..a1c1dd8328 --- /dev/null +++ b/translated/tech/20150211 25 Tips for Intermediate Git Users.md @@ -0,0 +1,469 @@ +25个给git熟手的技巧 +================================================================================ +我已经使用git差不多18个月了,觉得自己对它应该已经非常了解。然后来自GitHub的[Scott Chacon][1]过来给LVS做培训,[LVS是一个赌博软件供应商和开发商][2](从2013年开始的合同),而我在第一天里就学到了很多。 + +作为一个对git感觉良好的人,我觉得分享从社区里掌握的一些有价值的信息,也许能帮某人解决问题而不用做太深入研究。 + +### 基本技巧 ### + +#### 1. 安装后的第一步 #### + +在安装好git后,你第一件该做的事是设置你的名字和电子邮箱,因为每次提交都要用到这些信息: + + $ git config --global user.name "Some One" + $ git config --global user.email "someone@gmail.com" + +#### 2. Git是基于指针的 #### + +保存在git里的一切都是文件。当你创建一个提交的时候,会建立一个包含你的提交信息和相关数据(名字,邮件地址,日期/时间,前一个提交,等等)的文件,并把它链接到一个文件树中。文件树中包含了对象或其他树的列表。对象或容器是和本次提交相关的实际内容(也是一个文件,你想了解的话,尽管文件名并没有包含在对象里,而是在树中)。所有这些文件都使用对象的SHA-1哈希值作为文件名。 + +用这种方式,分支和标签就是简单的文件(基本上是这样),包含指向实际提交的SHA-1哈希值。使用这些索引会带来优秀的灵活性和速度,比如创建一个新分支就只要简单地创建一个包含分支名字和所分出的那个提交的SHA-1索引的文件。当然,你不需要自己做这些,而只要使用Git命令行工具(或者GUI),但是实际上就是这么简单。 + +你也许听说过叫HEAD的索引。这只是简单的一个文件,包含了你当前指向的那个提交的SHA-1索引值。如果你正在解决一次合并冲突然后看到了HEAD,这并不是一个特别的分支或分值上一个必须的特殊点,只是标明你当前所在位置。 + +所有的分支指针都保存在.git/refs/heads里,HEAD在.git/HEAD里,而标签保存在.git/refs/tags里 - 自己可以放心地进去看看。 + +#### 3. 两个父节点 - 当然! #### + +在历史中查看一个合并提交的信息时,你将看到有两个父节点(相对于一般工作上的常规提交的情况)。第一个父节点是你所在的分支,第二个是你合并过来的分支。 + +#### 4. 合并冲突 #### + +目前我相信你碰到过合并冲突并且解决过。通常是编辑一下文件,去掉<<<<,====,>>>>标志,保留需要留下的代码。有时能够看到这两个修改之前的代码会很不错,比如,在这两个分支上有冲突的改动之前。下面是一种方式: + + $ git diff --merge + diff --cc dummy.rb + index 5175dde,0c65895..4a00477 + --- a/dummy.rb + +++ b/dummy.rb + @@@ -1,5 -1,5 +1,5 @@@ + class MyFoo + def say + - puts "Bonjour" + - puts "Hello world" + ++ puts "Annyong Haseyo" + end + end + +如果是二进制文件,比较差异就没那么简单了...通常你要做的就是测试这个二进制文件的两个版本来决定保留哪个(或者在二进制文件编辑器里手工复制冲突部分)。从一个特定分支获取文件拷贝(比如说你在合并master和feature123): + + $ git checkout master flash/foo.fla # 或者... + $ git checkout feature132 flash/foo.fla + $ # 然后... + $ git add flash/foo.fla + +另一种方式是通过git输出文件 - 你可以输出到另外的文件名,然后再重命名正确的文件(当你决定了要用哪个)为正常的文件名: + + $ git show master:flash/foo.fla > master-foo.fla + $ git show feature132:flash/foo.fla > feature132-foo.fla + $ # 检出master-foo.fla和feature132-foo.fla + $ # 假如说我们决定来自feature132的文件是正确的 + $ rm flash/foo.fla + $ mv feature132-foo.fla flash/foo.fla + $ rm master-foo.fla + $ git add flash/foo.fla + +更新:感谢Carl在原博客文章上评论里的提醒,你实际上可以用“git checkout —ours flash/foo.fla”和“git checkout —theirs flash/foo.fla”来检出特定版本的文件,而不用记住你在合并的分支名字。就我个人来说喜欢更精确一点,但这也是一种方式... + +记着在解决完冲突后要将文件加入提交(像我上面做的那样)。 + +### 服务器,分支和标签 ### + +#### 5. 远端服务器 #### + +git的一个超强大的功能就是可以有不止一个远端服务器(实际上你一直都在一个本地仓库上工作)。你并不是一定都要有写权限,你可以有多个可以读取的服务器(用来合并他们的工作)然后写入其他仓库。添加一个新的远端服务器很简单: + + $ git remote add john git@github.com:johnsomeone/someproject.git + +如果你想查看远端服务器的信息可以这样做: + + # 显示每个远端服务器的URL + $ git remote -v + + # 提供更多详细信息 + $ git remote show name + +你随时都可以查看本地分支和远端分支的差异: + + $ git diff master..john/master + +你也可以查看不在远端分支的HEAD的改动: + + $ git log remote/branch.. + # 注意:..后面没有结束的refspec + +#### 6. 标签 #### + +在git里有两种类型的标签 - 轻量级标签和带注释标签。记住技巧2里说过git是基于指针的,这两者之间的差异也很简单。轻量级标签只是一个简单的指向一次提交的带名字指针。你随时都可以将它指向另一个提交。带注释标签是一个指向标签对象的带名字指针,带有自己的信息和历史。因为有自己的信息,它可以根据需要用GPG签名。 + +建立这两种类型的标签都很简单(只有一个命令行开关的差异) + + $ git tag to-be-tested + $ git tag -a v1.1.0 # 会提示输入标签信息 + +#### 7. 建立分支 #### + +在git里建立分支非常简单(而且像闪电一样快,因为它只需要创建一个小于100字节的文件)。用普通方式建立新分支并切换过去: + + $ git branch feature132 + $ git checkout feature132 + +当然,如果你确定自己要新建分支并直接切换过去,可以用一个命令实现: + + $ git checkout -b feature132 + +如果你想重命名一个本地分支也很简单(可以显示发生了什么的较长的方式): + + $ git checkout -b twitter-experiment feature132 + $ git branch -d feature132 + +更新:你也可以(像Brian Palmer在原博客文章的评论里提出的)只用“git branch”的-m开关在一个命令里实现(像Mike提出的,如果你只有一个分支参数,就会重命名当前分支): + + $ git branch -m twitter-experiment + $ git branch -m feature132 twitter-experiment + +#### 8. 合并分支 #### + +在将来什么时候,你希望合并改动。有两种方式: + + $ git checkout master + $ git merge feature83 # 或者... + $ git rebase feature83 + +merge和rebase之间的差别是merge会尝试处理改动并建立一个新的混合了两者的提交。rebase会尝试把你从一个分支最后一次分离后的所有改动,一个个加到该分支的HEAD上。不过,在已经将分支推到远端服务器后不要再rebase了 - 这回引起冲突/问题。 + +如果你不确定在哪些分支上还有独有的工作 - 所以你也不知道哪些分支需要合并而哪些可以删除,git branch有两个开关可以帮你: + + # 显示已经全部合并到当前分支的分支 + $ git branch --merged + + # 显示没有合并到当前分支的分支 + $ git branch --no-merged + +#### 9. 远端分支 #### + +如果你在本地有一个分支希望推到远端服务器上,你可以用一行命令推送上去: + + $ git push origin twitter-experiment:refs/heads/twitter-experiment + # origin是我们服务器的名字,而twitter-experiment是分支名字 + +更新:感谢Erlend在原博客文章上的评论 - 这个实际上和`git push origin twitter-experiment`效果一样,不过使用完整的语法,你可以在两者之间使用不同的分知名(这样本地分支可以是`add-ssl-support`而远端是`issue-1723`)。 + +如果你想在远端服务器上删除一个分支(注意分支名前面的冒号): + + $ git push origin :twitter-experiment + +如果你想查看所有远端分支的状态可以这样做: + + $ git remote show origin + +这个命令可能会列出服务器上一些以前有过但现在已经不在了的分支。如果碰到这种情况你可以用下面的命令从你本地分支里清理掉: + + $ git remote prune + +最后,如果你想在本地跟踪一个远端分支,普通的方式是: + + $ git branch --track myfeature origin/myfeature + $ git checkout myfeature + +不过,新版的git在使用-b标记检出分支时会自动设定跟踪: + + $ git checkout -b myfeature origin/myfeature + +### 在储藏点,索引和文件系统中保存内容 ### + +#### 10. 储藏 #### + +在git里你可以把当前工作状态放进一个储藏堆栈中,然后可以再取出来。最简单的情形是下面这样: + + $ git stash + # 做点其他事情... + $ git stash pop + +许多人建议使用`git stash apply`来代替pop,不过如果这样做的话最后会遗留一个很长的储藏列表。而“pop”会在全部加载后自动从堆栈中移除。如果使用过`git stash apply`,你也可以使用下面的命令从堆栈上移除最后一项: + + $ git stash drop + +git会基于当前的提交信息自动创建评论。如果你更希望有自定义信息的话(因为它可能和前一个提交没有任何联系): + + $ git stash save "My stash message" + +如果你希望从列表中取出一个特定的储藏点(不一定非得是最后一个)可以先列出它们然后用下面的方式取出: + + $ git stash list + stash@{0}: On master: Changed to German + stash@{1}: On master: Language is now Italian + $ git stash apply stash@{1} + +#### 11. 交互式添加 #### + +在subversion的世界里你只能修改文件然后提交所有改动。而在git里你有强大得多的方式来提交部分文件或者甚至是部分补丁。提交部分文件或文件中的部分改动你需要进入交互式模式: + + $ git add -i + staged unstaged path + + + *** Commands *** + 1: status 2: update 3: revert 4: add untracked + 5: patch 6: diff 7: quit 8: help + What now> + +这会让你进入一个基于菜单的交互式提示。你可以使用命令中的数字或高亮的字母(如果你在终端里打开了高亮的话)来进入相应的模式。然后就只是输入你希望操作的文件的数字了(你可以使用这样的格式,1或者1-4或2,4,7)。 + +如果你想进入补丁模式(交互式模式下的‘p’或‘5’),你也可以直接进入: + + $ git add -p + diff --git a/dummy.rb b/dummy.rb + index 4a00477..f856fb0 100644 + --- a/dummy.rb + +++ b/dummy.rb + @@ -1,5 +1,5 @@ + class MyFoo + def say + - puts "Annyong Haseyo" + + puts "Guten Tag" + end + end + Stage this hunk [y,n,q,a,d,/,e,?]? + +你可以看到下方会有一些选项供选择用来添加该文件的这个改动,该文件的所有改动,等等。使用‘?’命令可以详细解释这些选项。 + +#### 12. 从文件系统里保存/取回改动 #### + +有些项目(比如git项目本身)在git文件系统中直接保存额外文件而并没有将它们加入到版本控制中。 + +让我们从在git中存储一个随机文件开始: + + $ echo "Foo" | git hash-object -w --stdin + 51fc03a9bb365fae74fd2bf66517b30bf48020cb + +这样这个目标文件就已经保存到数据库中了,但是如果你没有设定一个指向它的指针的话它会被当做垃圾回收。最简单的方式是设定一个标签: + + $ git tag myfile 51fc03a9bb365fae74fd2bf66517b30bf48020cb + +注意这里我们使用了标签myfile。当我们需要使用这个文件的时候可以这样做: + + $ git cat-file blob myfile + +这个对于一些工具文件很有用,开发者可能会用到(密码,GPG密钥,等等)但是又不希望每次都检出到硬盘(尤其是在实际工作中)。 + +### 日志以及有哪些改动? ### + +#### 13. 查看日志 #### + +如果不用‘git log’来查看最近的提交你git用不了多久。不过,有一些技巧来更好地应用。比如,你可以使用下面的命令来查看每次提交的具体改动: + + $ git log -p + +或者你可以仅仅查看有哪些文件改动: + + $ git log --stat + +有个很不错的别名你可以试试,会显示简短提交名和一个不错的分支图并在一行里显示提交信息(有点像gitk,但是是在命令行下): + + $ git config --global alias.lol "log --pretty=oneline --abbrev-commit --graph --decorate" + $ git lol + * 4d2409a (master) Oops, meant that to be in Korean + * 169b845 Hello world + +#### 14. 搜索日志 #### + +如果你想找特定作者可以这样做: + + $ git log --author=Andy + +更新:感谢Johannes的评论,我已经去掉了之前这里的一些有混淆的地方。 + +或者你想在提交信息里找一些相关字段: + + $ git log --grep="Something in the message" + +也有一个更强大的叫做pickaxe的命令用来查找删除或添加某个特定内容的提交(比如,该文件第一次出现或被删除)。这可以告诉你什么时候增加了一行(但这一行里的某个字符后面被改动过就不行了): + + $ git log -S "TODO: Check for admin status" + +假如你改动了一个特定的文件,比如`lib/foo.rb` + + $ git log lib/foo.rb + +比如说你有一个`feature/132`分支和`feature/145`分支,然后你想看看这两个分支上不在master分支里的提交(注意^符号是不在的意思): + + $ git log feature/132 feature/145 ^master + +你也可以使用ActiveSupport格式的日期来缩小到某个日期范围: + + $ git log --since=2.months.ago --until=1.day.ago + +默认情况下会用OR来组合查询,但你可以轻易地改为AND(如果你有超过一条的标准) + + $ git log --since=2.months.ago --until=1.day.ago --author=andy -S "something" --all-match + +#### 15. 查看/修改版本 #### + +有很多方式可以用来引用一个版本,看你记得哪个: + + $ git show 12a86bc38 # 根据版本 + $ git show v1.0.1 # 根据标签 + $ git show feature132 # 根据分支名 + $ git show 12a86bc38^ # 一次提交的父节点 + $ git show 12a86bc38~2 # 一次提交的祖父节点 + $ git show feature132@{yesterday} # 时间相关 + $ git show feature132@{2.hours.ago} # 时间相关 + +注意和之前部分有些不同,末尾的插入符号意思是该提交的父节点 - 开始位置的插入符号意思是不在这个分支。 + +#### 16. 选择范围 #### + +最简单的方式: + + $ git log origin/master..new + # [old]..[new] - 所有你还没有推送的提交 + +你也可以省略[new],将使用当前的HEAD。 + +### Rewinding Time & Fixing Mistakes ### + +#### 17. 重置改动 #### + +如果你还没有提交的话可以用下面的命令轻松地取消改动: + + $ git reset HEAD lib/foo.rb + +通常会使用‘unstage’的别名,因为看上去有些不直观。 + + $ git config --global alias.unstage "reset HEAD" + $ git unstage lib/foo.rb + +如果你已经提交了该文件,你可以做两件事 - 如果是最后一次提交你还可以改正: + + $ git commit --amend + +这会取消最后一次提交,把工作分支回退到提交前标记了所有改动的状态,而且提交信息也都准备好可以修改或直接提交。 + +如果你已经提交过多次而且希望全部回退,你可以将分支重置到合适的位置。 + + $ git checkout feature132 + $ git reset --hard HEAD~2 + +如果你实际上希望将分支指向一个完全不同的SHA1(也许你要将一个分支的HEAD替换到另一个分支,或者之后的某次提交)你可以使用下面的较长的方式: + + $ git checkout FOO + $ git reset --hard SHA + +实际上有一个快速的方式(不需要先把你的工作分支切换到FOO再前进到SHA): + + $ git update-ref refs/heads/FOO SHA + +#### 18. 提交到了错误的分支 #### + +好吧,假如说你已经提交到了master,但却应该创建一个叫experimental的主题分支更合适。要移动这些改动,你可以在当前位置创建分支,回退HEAD再检出新分支: + + $ git branch experimental # 创建一个指向当前master的位置的指针 + $ git reset --hard master~3 # 移动master分支的指针到3个版本之前 + $ git checkout experimental + +如果你的改动是在分支的分支的分支上会更复杂。那样你需要做的是将分支基础切换到其他地方: + + $ git branch newtopic STARTPOINT + $ git rebase oldtopic --onto newtopic + +#### 19. 交互式切换基础 #### + +这是一个我之前看过展示却没真正理解过的很赞的功能,现在很简单。假如说你提交了3次但是你希望更改顺序或编辑(或者合并): + + $ git rebase -i master~3 + +然后会启动你的编辑器并带有一些指令。你所要做的就是修改这些指令来选择/插入/编辑(或者删除)提交和保存/退出。然后在编辑完后你可以用`git rebase --continue`命令来让每一条指令生效。 + +如果你有修改,将会切换到你提交时所处的状态,之后你需要使用命令git commit --amend来编辑。 + +**注意:在rebase的时候千万不要提交 - 只能先添加然后使用参数--continue,--skip或--abort。** + +#### 20. 清理 #### + +如果你提交了一些内容到你的分支(也许你从SVN导入了一些旧仓库),然后你希望把某个文件从历史记录中全部删掉: + + $ git filter-branch --tree-filter 'rm -f *.class' HEAD + +如果你已经推送到origin了,但之后提交了一些垃圾改动,你也可以在推送前在本地系统里这样做: + + $ git filter-branch --tree-filter 'rm -f *.class' origin/master..HEAD + +### 其他技巧 ### + +#### 21. 你查看过的前一个引用 #### + +如果你知道自己之前查看过一个SHA-1,但是随后做了一些重置/回退的操作,你可以使用reflog命令来列出最近查看过的SHA-1记录: + + $ git reflog + $ git log -g # 和上面一样,但是使用'log'格式输出 + +#### 22. 分支命名 #### + +一个可爱的小技巧 - 别忘了分支名并不限于a-z和0-9。名字中可以用/和.将非常方便用来建立伪命名空间或版本,例如: + + $ # 生成版本132的改动历史 + $ git shortlog release/132 ^release/131 + $ # 贴上v1.0.1的标签 + $ git tag v1.0.1 release/132 + +#### 23. 找出谁是凶手 #### + +通常找出来谁改动了某个文件里的某行代码会很有用。实现这个功能的最简单命令是: + + $ git blame FILE + +有时候这些改动来自其他文件(如果你合并了两个文件,或者你移动了某个函数)所以你可以使用下面的命令: + + $ # 显示内容来自哪个文件 + $ git blame -C FILE + +有时候通过点击各个改动然后回到很早很早以前来跟踪改动会很不错。有一个很好的内建GUI命令来做这个: + + $ git gui blame FILE + +#### 24. 数据维护 #### + +通常git不需要经常维护,它把自己照顾的很好。不过,你可以通过下面的命令查看数据统计: + + $ git count-objects -v + +如果占用很多空间的话,你可以选择在你的本地仓库做垃圾回收。这不会影响推送或其他人,却会让一些命令运行更快而且减少空间占用: + + $ git gc + +经常运行完整性检查也很有意义: + + $ git fsck --full + +你也可以在末尾加上`--auto`参数(如果你在服务器上通过crontab经常/每天都运行这个命令的话),然后它只会在必要的时候才执行fsck动作。 + +在检查的时候,看到“dangling”或“unreachable”是正常的,通常这是由回退HEAD或切换基础的结果。而看到“missing”或“sha1 mismatch”就不对了...找专业人士帮忙吧! + +#### 25. 恢复遗失的分支 #### + +如果你使用-D参数删除了experimental分支,可以用下面的命令重新建立: + + $ git branch experimental SHA1_OF_HASH + +如果你访问过的话,你通常可以用git reflog来找到SHA1哈希值。 + +另一种方式是使用`git fsck —lost-found`。其中一个dangling的提交就是丢失的HEAD(它只是已删除分支的HEAD,而HEAD^被引用为当前的HEAD所以它并不处于dangling状态) + +### 搞定! ### + +哇,这是我写过的最长的一篇博客,我希望有人能觉得有用。如果你这么觉得,或者你有任何疑问请在评论里留言让我知道... + +-------------------------------------------------------------------------------- + +via: https://www.andyjeffries.co.uk/25-tips-for-intermediate-git-users/ + +作者:[Andy Jeffries][a] +译者:[zpl1025](https://github.com/zpl1025) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出 + +[a]:https://www.andyjeffries.co.uk/author/andy-jeffries/ +[1]:http://gitcasts.com/about +[2]:http://www.lvs.co.uk/