modified: FGI/OYK.pm
[GalaxyCodeBases.git] / gitips.md
blob71c368d15984d6c99c602f9afc8760beed2fea8f
1 Detach subdirectory into separate Git repository
2 ======
3 http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository
5 ### The Easy Way™
7 It turns out that this is such a common and useful practice that the overlords of git made it really easy, but you have to have a newer version of git (>= 1.7.11 May 2012). See the **appendix** for how to install the latest git. Also, there's a **real-world example** in the **walkthrough** below.
9 0. Prepare the old repo
11         pushd <big-repo>
12         git subtree split -P <name-of-folder> -b <name-of-new-branch>
13         popd
15   **Note:** `<name-of-folder>` must NOT contain leading or trailing characters.  For instance, the folder named `subproject` MUST be passed as `subproject`, NOT `./subproject/`
17   **Note for windows users:** when your folder depth is > 1, `<name-of-folder>` must have *nix style folder separator (/). For instance, the folder named `path1\path2\subproject` MUST be passed as `path1/path2/subproject`
19 0. Create the new repo
21         mkdir <new-repo>
22         pushd <new-repo>
24         git init
25         git pull </path/to/big-repo> <name-of-new-branch>
26     
27 0. Link the new repo to Github or wherever
29         git remote add origin <git@github.com:my-user/new-repo.git>
30         git push origin -u master
32 0. Cleanup, *if desired*
34         popd # get out of <new-repo>
35         pushd <big-repo>
37         git rm -rf <name-of-folder>
39   **Note**: This leaves all the historical references in the repository.See the **Appendix** below if you're actually concerned about having committed a password or you need to decreasing the file size of your `.git` folder.
41 ...
43 ### Walkthrough
45 These are the **same steps as above**, but following my exact steps for my repository instead of using `<meta-named-things>`.
47 Here's a project I have for implementing JavaScript browser modules in node:
49     tree ~/Code/node-browser-compat
50     
51     node-browser-compat
52     ├── ArrayBuffer
53     ├── Audio
54     ├── Blob
55     ├── FormData
56     ├── atob
57     ├── btoa
58     ├── location
59     └── navigator
61 I want to split out a single folder, `btoa`, into a separate git repository
63     pushd ~/Code/node-browser-compat/
64     git subtree split -P btoa -b btoa-only
65     popd
67 I now have a new branch, `btoa-only`, that only has commits for `btoa` and I want to create a new repository.
69     mkdir ~/Code/btoa/
70     pushd ~/Code/btoa/
71     git init
72     git pull ~/Code/node-browser-compat btoa-only
74 Next I create a new repo on Github or bitbucket, or whatever and add it is the `origin` (btw, "origin" is just a convention, not part of the command - you could call it "remote-server" or whatever you like)
76     git remote add origin git@github.com:node-browser-compat/btoa.git
77     git push origin -u master
79 Happy day!
81 **Note:** If you created a repo with a `README.md`, `.gitignore` and `LICENSE`, you will need to pull first:
83     git pull origin -u master
84     git push origin -u master
86 Lastly, I'll want to remove the folder from the bigger repo
88     git rm -rf btoa
90 ...
92 ### Appendix
94 #### Latest git on OS X
96 To get the latest version of git:
98     brew install git
100 To get brew for OS X:
102 <http://brew.sh>
104 #### Latest git on Ubuntu
106     sudo apt-get update
107     sudo apt-get install git
108     git --version
110 If that doesn't work (you have a very old version of ubuntu), try
112     sudo add-apt-repository ppa:git-core/ppa
113     sudo apt-get update
114     sudo apt-get install git
116 If that still doesn't work, try
118     sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
119     sudo ln -s \
120     /usr/share/doc/git/contrib/subtree/git-subtree.sh \
121     /usr/lib/git-core/git-subtree
123 Thanks to rui.araujo from the comments.
125 #### clearing your history
127 By default removing files from git doesn't actually remove them from git, it just commits that they aren't there anymore. If you want to actually remove the historical references (i.e. you have a committed a password), you need to do this:
129     git filter-branch --tree-filter 'rm -rf <name-of-folder>' HEAD
131 After that you can check that your file or folder no longer shows up in the git history at all
133     git log -S<name-of-folder> # should show nothing
135 However, you **can't "push" deletes to github** and the like. If you try you'll get an error and you'll have to `git pull` before you can `git push` - and then you're back to having everything in your history.
137 So if you want to delete history from the "origin" - meaning to delete it from github, bitbucket, etc - you'll need to delete the repo and re-push a pruned copy of the repo. But wait - **there's more**! - If you're really concerned about getting rid of a password or something like that you'll need to prune the backup (see below).
139 #### making `.git` smaller
141 The aforementioned delete history command still leaves behind a bunch of backup files - because git is all too kind in helping you to not ruin your repo by accident. It will eventually deleted orphaned files over the days and months, but it leaves them there for a while in case you realize that you accidentally deleted something you didn't want to.
143 So if you really want to *empty the trash* to **reduce the clone size** of a repo immediately you have to do all of this really weird stuff:
145     rm -rf .git/refs/original/ && \
146     git reflog expire --all && \
147     git gc --aggressive --prune=now
149     git reflog expire --all --expire-unreachable=0
150     git repack -A -d
151     git prune
153 That said, I'd recommend not performing these steps unless you know that you need to - just in case you did prune the wrong subdirectory, y'know? The backup files shouldn't get cloned when you push the repo, they'll just be in your local copy.
155 ### Credit
157   * http://psionides.eu/2010/02/04/sharing-code-between-projects-with-git-subtree/
158   * http://stackoverflow.com/questions/1216733/remove-a-directory-permanently-from-git
159   * http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/
160   * http://stackoverflow.com/questions/1904860/how-to-remove-unreferenced-blobs-from-my-git-repo
164 How do you merge two git repositories ?
165 ======
166 http://stackoverflow.com/questions/1425892/how-do-you-merge-two-git-repositories
168 A single branch of another repository can be easily placed under a subdirectory retaining its history. For example:
170     git subtree add --prefix=rails git://github.com/rails/rails.git master
172 This will appear as a single commit where all files of Rails master branch are added into "rails" directory.
173 However the commit's title contains a reference to the old history tree.
175     Add 'rails/' from commit <rev>
177 Where `<rev>` is a SHA-1 commit hash. You can still see the history, blame some changes.
179     git log <rev>
180     git blame <rev> -- README.md
182 Note that you can't see the directory prefix from here since this is an actual old branch left intact.
183 You should treat this like a usual file move commit: you will need an extra jump when reaching it.
185     # finishes with all files added at once commit
186     git log rails/README.md
187     
188     # then continue from original tree
189     git log <rev> -- README.md
191 There are more complex solutions like doing this manually or rewriting the history as described in other answers.
193 The git-subtree command is a part of official git-contrib, some packet managers install it by default (OS X Homebrew).
194 But you might have to install it by yourself in addition to git.
198 Changing remote repository for a git submodule
199 ======
200 http://stackoverflow.com/questions/913701/changing-remote-repository-for-a-git-submodule
202 What worked for me (on Windows, using git version 1.8.3.msysgit.0):
204  - Update .gitmodules with the path to the new repository
205  - Remove the corresponding line from the ".git/config" file
206  - Delete the corresponding directory in the ".git/modules/external" directory
207  - Delete the checked out submodule directory itself (unsure if this is necessary)
208  - Run `git submodule init` and `git submodule update`
209  - Make sure the checked out submodule is at the correct commit, and commit that, since it's likely that the hash will be different
211 After doing all that, everything is in the state I would expect. I imagine other users of the repository will have similar pain when they come to update though - it would be wise to explain these steps in your commit message!
215 How do I remove a Git submodule?
216 ======
217 http://stackoverflow.com/questions/1260748/how-do-i-remove-a-git-submodule/16162000#16162000
219 Since [git1.8.3 (April 22d, 2013)][1]:
221 > There was no Porcelain way to say "I no longer am interested in this submodule", once you express your interest in a submodule with "`submodule init`".  
222 "**`submodule deinit`**" is the way to do so.
224 The deletion process also uses `git rm` (since git1.8.5 October 2013).  
226 The all removal process would then be:
228     git submodule deinit asubmodule    
229     git rm asubmodule
230     # Note: asubmodule (no trailing slash)
231     # or, if you want to leave it in your working tree
232     git rm --cached asubmodule
234 But you seem to still need a:
236     rm -rf .git/modules/asubmodule
238 This is mentioned in [Daniel Schroeder][2]'s [answer][3], and summarized by [Eonil][4] in [the comments][5]:
240 > This leaves `.git/modules/<path-to-submodule>/` unchanged.  
241 So if you once delete a submodule with this method and re-add them again, it will not be possible because repository already been corrupted.
243 ----
245 `git rm`: See [commit 95c16418][6]:
247 > Currently using "`git rm`" on a submodule removes the submodule's work tree from that of the superproject and the gitlink from the index.  
248 But the submodule's section in `.gitmodules` is left untouched, which is a leftover of the now removed submodule and might irritate users (as opposed to the setting in `.git/config`, this must stay as a reminder that the user showed interest in this submodule so it will be repopulated later when an older commit is checked out).
250 > Let "`git rm`" help the user by not only removing the submodule from the work tree but by also removing the "`submodule.<submodule name>`" section from the .gitmodules file and stage both.
252 -----
254 `git submodule deinit`: It stems from [this patch][7]:
256 > With "`git submodule init`" the user is able to tell git he cares about one or more submodules and wants to have it populated on the next call to "`git submodule update`".  
257 But currently there is no easy way he could tell git he does not care about a submodule anymore and wants to get rid of his local work tree (except he knows a lot about submodule internals and removes the "`submodule.$name.url`" setting from `.git/config` together with the work tree himself).
259 > Help those users by providing a '**`deinit`**' command.  
260 This **removes the whole `submodule.<name>` section from `.git/config` either for the given
261 submodule(s)** (or for all those which have been initialized if '`.`' is given).  
262 Fail if the current work tree contains modifications unless forced.  
263 Complain when for a submodule given on the command line the url setting can't be found in `.git/config`, but nonetheless don't fail. 
265 This takes care if the (de)initialization steps (`.git/config` and `.git/modules/xxx`)
267 Since git1.8.5, the `git rm` takes *also* care of the:
269 - '`add`' step which records the url of a submodule in the `.gitmodules` file: it is need to removed for you.
270 - the submodule **[special entry][8]** (as illustrated by [this question][9]): the git rm removes it from the index:  
271 `git rm --cached path_to_submodule` (no trailing slash)  
272 That will remove that directory stored in the index with a special mode "160000", marking it as a submodule root directory.
274 If you forget that last step, and try to add what was a submodule as a regular directory, you would get error message like:
276     git add mysubmodule/file.txt 
277     Path 'mysubmodule/file.txt' is in submodule 'mysubmodule'
280   [1]: https://github.com/git/git/blob/v1.8.3-rc0/Documentation/RelNotes/1.8.3.txt#L135-L137
281   [2]: http://stackoverflow.com/users/2753241/daniel-schroeder
282   [3]: http://stackoverflow.com/a/26505847/6309
283   [4]: http://stackoverflow.com/users/246776/eonil
284   [5]: http://stackoverflow.com/questions/1260748/how-do-i-remove-a-git-submodule/16162000?noredirect=1#comment41729982_16162000
285   [6]: https://github.com/git/git/commit/95c16418f0375e2fc325f32c3d7578fba9cfd7ef
286   [7]: http://git.661346.n2.nabble.com/PATCH-v3-submodule-add-deinit-command-td7576946.html
287   [8]: http://stackoverflow.com/questions/1992018/git-submodule-update-needed-only-initially/2227598#2227598
288   [9]: http://stackoverflow.com/q/16574625/6309
292 How to undo the last commit ?
293 ======
294 http://stackoverflow.com/questions/927358/how-to-undo-the-last-commit
296 From the docs for [`git-reset`][91]:
298 > ### Undo a commit and redo
300 >     $ git commit ...              (1)
301 >     $ git reset --soft HEAD~1     (2)
302 >     << edit files as necessary >> (3)
303 >     $ git add ....                (4)
304 >     $ git commit -c ORIG_HEAD     (5)
306 > 1. This is what you want to undo
308 > 2. This is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message<sup>1</sup>, or both. Leaves working tree as it was before "commit".
310 > 3. Make corrections to working tree files.
312 > 4. Stage changes for commit.
314 > 5. Commit the changes, reusing the old commit message. `reset` copied the old head to `.git/ORIG_HEAD`; `commit` with `-c ORIG_HEAD` will open an editor, which initially contains the log message from the old commit and allows you to edit it. If you do not need to edit the message, you could use the `-C` option instead.
316 -----
318 <sup>Editor's note 1</sup>: You don't need to reset to the an earlier commit if "you misspelled your commit message". If you `reset`, git will not link new activity to the previous commit in any way, giving you a blank slate for a new commit message. The easier option is [`git commit --amend`][92], which will open your default commit message editor pre-populated with the last commit message. 
320 Beware however that if you have added any new changes to the index, using `commit --amend` will add them to your previous commit.
323   [91]: http://git-scm.com/docs/git-reset
324   [92]: http://stackoverflow.com/q/179123/1146608