aboutsummaryrefslogtreecommitdiff
path: root/cheatsheet.txt
blob: 592d8d9b49866122de3789f50432b4833b186aab (plain)
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
Global Setup

 See also Submodules and History Cleanup configurations below.

 git config --global user.name "First Last"
 git config --global user.email first@example.com

 git config --global color.status.branch magenta


 # This makes sure that changes to tags are also fetched. The second
 # version in addition makes sure they are overriden (required from
 # git 2.20.0).
 #
 git config --global remote.origin.tagopt --tags

 git config --global remote.origin.fetch '+refs/tags/*:refs/tags/*'
 git config --global --unset remote.origin.tagopt

 # Enable hooks globally.
 #
 git config --global core.hooksPath <git-project-dir>/hooks

 # Override the global hooks with the repository-specific hooks (run in a
 # repository directory).
 #
 git config core.hooksPath .git/hooks

Undo (move to index) last commit

  git reset --soft HEAD~

Tag

  git tag -a X.Y.Z -m "Tag version X.Y.Z"

Squash multiple commits into one

  git rebase -i HEAD~<N>

  http://www.gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html

Change commit message

  git commit --amend

Revert uncommited changes

  git reset --hard HEAD

Copy commit from one branch to the other

  # On the source branch
  git log -1
  git cout <target-branch>
  git cherry-pick <commit-id>

Setup remote repository

  On remote:

  1. Use the server/mkrepo.sh script:

     mkrepo.sh <name>
     mkrepo.sh --private <name>

     <name> is without the .git suffix.

  On local:

  1. git remote add origin git.codesynthesis.com:/var/scm/proj/proj.git
  2. git push --tags origin master
  3. # blow the local project and do clone
     git clone git.codesynthesis.com:/var/scm/proj/proj.git [name]

Setup severe git account (replace USER)

# adduser --disabled-password --shell /usr/bin/git-shell USER
# usermod -a -G scm USER
# su -l -s /bin/bash USER
$ mkdir .ssh
$ mv /tmp/id_rsa.pub .ssh/authorized_keys
$ chown -R USER:USER .ssh
$ chmod 700 .ssh
$ chmod 600 .ssh/authorized_keys


Delete a branch from a remote repository

  git push origin :experimental

  Find a ref that matches experimental in the origin repository (e.g.
  refs/heads/experimental), and delete it.

  Using the push.sh script:

  ./push.sh :<name>

Rebasing

  Local (e.g., from a feature branch to master):

  git rebase <src> [<dst>]

  If <dst> is not specified, current branch is used. If <dst> is
  specified, it is checked out.

  Remote (e.g., merge someone else's changes):

  git fetch
  git rebase origin[/master]
  git push --tags origin

History Cleanup

  This is the overall process for cleaning up the history in a feature
  branch before merging it into master. The goal is to end up with a
  nice and clean history in master, without any divergences and merges
  that usually the result of the git merge command.

  Note: make sure you have rerere enabled for this process to work
  smoothly:

  git config --global rerere.enabled true

  Note: if you are just learning this procedure, make a local backup
  copy of your repository in case things go badly and you need to
  start from scratch.

  To achieve this we first merge any unmerged changes that may be on
  master and then rebase the whole thing on master. The end result of
  this step is a history that first has all the commits that are from
  master followed by all the commit from the feature branch (in other
  words, master is now a prefix of our history which means we can
  fastforward-only (--ff-only) merge this history into it):

  git cout master
  git pull
  git cout feature
  git merge master
  git rebase master

  The last step ('git rebase') is where the rerere machinery mentioned
  earlier may have to come to play and if you didn't have it enabled
  during the development of, things may not go very smoothly and we
  need this side note: in general, rebase is no magic, what it does is
  take the patches from commits that you have and try to replay them
  on top of your new base, resolving conflicts just like merge does.

  So, if during development of your feature branch you had a merge
  conflicts that you had to manually resolve, then you will have
  merge conflicts during rebase that you will have to resolve as
  well.

  This is where rerere comes in. What it does is record your conflict
  resolutions when you first manually resolved them during your feature
  branch development. When later, during rebase, it sees that the same
  conflict is being resolved during rebase, it automatically applies
  the stored resolution. So if you didn't have rerere enabled from the
  beginning, this should explain why things didn't go smoothly for you:
  since you didn't have rerere enabled during your earlier merges, it
  didn't record the resolutions. And in this case what you would have
  gotten is a normal merge conflict, just like the first time, that
  you have to resolve manually again.

  Running 'git stat' should give you the idea of what needs fixing. Once
  you have edit the files, run 'git add' and then 'git rebase --continue'.

  Now, a note on rerere: again, it is kind of magic but not exactly.
  Specifically, it doesn't hide this whole process from you. Instead, it
  applies the conflict resolution that it has stored from earlier but
  stops for you to confirm that all is good. So what normally happens
  is that you still get the rebase merge conflict except that it applies
  the resolution and there is a message in git rebase output to this
  effect. Now you need to review it (e.g., with gitk), run 'git add'
  to confirm it's good, and then run 'git rebase --continue'. And of
  the side node.

  If you managed to get through all this, run gitk and verify that you
  now have a linear history that starts with master. If things got
  hairy along the way, you may want to verify that the end result,
  content-wise, is exactly the same as what you have started with
  by diff'ing with the backup you have made at the beginning.

  The second step is to clean up the commit history of our feature
  branch since it may have "dirty", work-in-progress commits. This may
  involve squashing several commits into one, rewording commit messages,
  and possibly reordering the commits. The swiss army knife for this
  is the interactive git rebase:

  git rebase -i HEAD~<N>

  Where <N> is the number of commits you would like to cleanup. Generally,
  if you want to work on all the commits on this feature branch run gitk
  and count the number of commits from the top to the first commit that
  is on master (and if you know a better way to achieve the same result,
  let me know). That is your <N>.

  Note also that the order of commits in the resulting edit file will
  be the reverse of what you see in gitk.

  Once in the edit file, use commands to squash, reword, or reorder
  the history (note that squashing several commits automatically
  means rewording the commit message). Note also that you can re-run
  this command multiple times. For example, you can squash some
  commits, then examine the history (with gitk), then squash some
  more, etc.

  Finally, once the history is cleaned up, we can merge it into
  master:

  git cout master
  git merge --ff-only feature

  Verify with gitk everything looks good on master and push:

  git push

  It is also a good idea to delete the feature branch, both locally
  and on the origin:

  git br -d feature
  git push origin :feature


Submodules

  Think of a submodule as a pointer to a specific commit (as opposed
  to something "latest") in another project. Moving this pointer to
  another commit is an explicit change to the containing project that
  we must perform and then commit. In many situations this pointer will
  be left "dangling", that is, the actual files corresponding to the
  commit won't be checked out unless we run an extra command or pass
  an extra option to make git do so.

  git config --global fetch.recurseSubmodules true
  git config --global status.submoduleSummary true
  git config --global diff.submodule log

  git submodule add ../<proj>.git <dir> # Project path interpreted relative to
                                        # our remote.

  git submodule update --init  # init and update
  git clone --recursive        # same as doing above manually

  git submodule update --remote [sub] # update submodule(s) (to remote master)
                                      # --rebase

  git fetch
  git checkout <commit-sha1> # As above but to specific commit.

  git pull             # fetches submodules, but does not update
  git submodule update # must be done explicitly (--init --recursive)!

  # Making changes, first make sure up-to-date with remote
  #
  git cout master # in submodule, to make changes, commit/push must be on both!

  # If already made changes (in sub/):
  git stash
  git cout master
  git stash pop

  # Remove submodule.
  #
  git submodule deinit sub
  git rm sub # then commit