forked from CRIStAL-PADR/reproducible-research-SE-notes
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathgit_practical.pillar
More file actions
231 lines (166 loc) · 11.2 KB
/
Copy pathgit_practical.pillar
File metadata and controls
231 lines (166 loc) · 11.2 KB
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
!! Practical Git by Scenarios
!!! Discarding your changes
It arrives the time for every woman/men to make mistakes and want to discard them.
Doing so may be dangerous, since once discarded you will not able to recover your changes.
It is however possible to instruct Git to do so.
For it, there are two Git comments that will perform the task for you and when combined they will completely discard every dirty file and directory in your repository:
==git reset== and ==git clean==
[[[language=bash
$ git reset --hard
$ git clean -df
]]]
The reason for needing two commands instead of one relies on the fact that Git has several staging areas (such as the ones used to keep the tracked files), which we usually would like to clean when we discard the repository. Of course, experienced readers may search why they would need both in Git's documentation.
!!! Exploring the History
!!!! The ==git log== command
The commit graphs we have shown so far are not evident at all while when we use the ==git status== command.
There is however a way to ask Git about them using the ==git log== command.
[[[language=bash
$ git log
commit 0c0e5ff55b56fe8eabc1661a1da64b41f9d74472
Author: Guille Polito <guillermopolito@gmail.com>
Date: Wed Mar 21 15:37:32 2018 +0100
Adding a title
commit 37adf4eaa945cbd7460991f88bff5aa902db06ce
Author: Guille Polito <guillermopolito@gmail.com>
Date: Wed Mar 21 14:02:43 2018 +0100
first version
]]]
==git log== prints the list of commits in order of parenthood.
The one on the top is the most recent commit, our last commit.
The one below is its parent, and so on.
As you can see, each commit has an id, the author name, the timestamp and its message.
We can also ask Git what are the changes introduced in a particular commit using the command ==git show==.
[[[language=bash
$ git show 0c0e5ff55b56fe8eabc1661a1da64b41f9d74472
commit 0c0e5ff55b56fe8eabc1661a1da64b41f9d74472
Author: Guille Polito <guillermopolito@gmail.com>
Date: Wed Mar 21 15:37:32 2018 +0100
Adding a title
diff --git a/README.md b/README.md
index e69de29..cad05f1 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+! a title
\ No newline at end of file
]]]
That will give us the commit description as in ==git log== plus a (not so readable) diff of the modified files showing the inserted, modified and deleted lines.
More advanced graphical tools are able to read this description and show a more user-friendly diff.
!!!! Seeing the history graph
Git's log provides a more graphish view on the terminal using some cute ascii art.
This view can be accesses through the ==git log \-\-graph \-\-oneline \-\-all== command.
Here is an example of this view for a more complex project.
In this view, stars represent the commits with their ids and commit messages, and lines represent the parenthood relationships.
[[[language=bash
$ git log --graph --oneline --all
* 4eb8446 Documenting
* e5a3e2e Add tests
* 680a79a Some other
| * ed4854f Merge pull request #1137
| |\
| | * 9e30e37 Some feature
| * | ba7f65c Merge pull request #1138
| |\ \
| | * | 31a40c4 Some Enhancement
| | |/
| * | 2d4698d Merge pull request #1139
| |\ \
| | * | 20c0ff4 Some fix
| | |/
| * | ae3ec45 Merge pull request #1136
]]]
However, we are not always in the mood of using the terminal, or of wanting to decode what was done in ascii art.
There are tools that are more suitable to explore the history of a project, usually providing some nice graphical capabilities.
This is the case of tools such as SourceTree (Figure *@commit_graph_sourcetree*) or Github's network view (Figure *@commit_graph_github_network*).
+Example of SourceTree's commit graph view>figures/sourcetree_tree.png|label=commit_graph_sourcetree+
+Example of Github's commit graph view>figures/github_network_tree.png|label=commit_graph_github_network+
!!! Ignoring files (100%)
@gitignore
Many times we will find that we do not want to commit some files that are in our repository's directory. This is mostly the case of generated or automatically downloaded files. For example, imagine you have a C project and some makefiles to compile it, generating a binary library. While it would be good to store the result of compilation from time to time, storing it in a Git repository (or SVN, or Bazar) may be a cause of headaches. First, as you will see in *@expert_git*, this may be a cause for conflicts.
Second, since we should be able to generated such binary library from the sources, having the already compiled result in the repository does not add so much value.
This same ideas can be used to ignore any kind of generated file. For example, pdfs generated by document generation tools, meta-data files generated by IDEs and tools (e.g., Eclipse), compiled libraries (e.g., dll, so, or dylib files).
In such cases, we can tell Git to ignore cetain files using the ==.gitignore== file.
The ==.gitignore== file is an optional text file that we can write in the root of our repository with a list of file paths to ignore.
[[[
# Example of .gitignore file
# Lines starting with hashtags are comments
# A file name will ignore that file
someignoredfile.txt
# A file name will ignore that file
someignoredfile.txt
# A file pattern will ignore all pdf files
*.pdf
]]]
Once your file is ready, you have to add it and commit it to Git to make it take effect.
[[[language=bash
$ git add .gitignore
$ git commit -m "Added gitignore"
]]]
From this moment on, all listed files will be ignored by ==git add== and ==git status==. And you will be able to perform further commands to add "all but ignored files":
[[[language=bash
$ git add .
]]]
!!! Getting out of Detached HEAD
Detached head means no other than "HEAD is not pointing to a branch".
Being in a detached HEAD state is not bad in itself, but it may provoke loss of changes.
As a matter of fact, any commit that is not properly referenced by another commit or by another Git reference (tag, branch) may be garbage collected.
Git will not forbid you to commit in this state, but any new commit you create will only reachable if you remember the commit hash.
To get out of dettached HEAD, the easiest solution is to checkout a branch, as we will see in the next section.
Checking out a branch will set HEAD to point to a branch instead of a commit, saving you some HEADaches.
!!! Accessing your Repository through SSH
@setupssh
To be able to access your repository from your local machine, you need to setup your credentials.
Think it this way: you need to tell the server who you are on every interaction you have with it.
Otherwise, Github will reject any operation against your repository.
Such a setup requires the creation and uploading of SSH keys.
An SSH key works as a lock: a key is actually a pair of a public and a private key. The private key is meant to reside in your machine and not be published at all. A public key is meant to be shared with others to prove your identity. Whenever you want to prove your identity, SSH will exchange messages encrypted with your public key, and see if you are able to decrypt it using your private key.
To create an SSH key, in *nix systems you can simply type in your terminal
[[[language=bash
$ ssh-keygen -t rsa -b 4096 -C "your_email@some_domain.com"
]]]
Follow the instructions in your terminal such as setting the location for your key pair (usually it is ==$HOME/.ssh==) and the passphrase (a kind of password). Finally, you'll end up with your public/private pair on the selected location. It is now time to upload it to Github.
Connect yourself to your Github settings (usually https://github.com/settings/profile) and go to the "SSH and GPG keys" menu. Import there the contents of your public key file. You should be now able to use your repository.
!!! Rewriting the history (Guille 100%)
Many times it happens that we accidentally commit something wrong.
Maybe we wanted to commit more or less things, maybe a completely different content, or we did a mistake in the commit's message. In these cases, we can rewrite Git's history, e.g, undo our current commit and go back to the previous commit, or rewrite the current commit with some new properties.
Be careful! Rewriting the history can have severe consequences. Imagine that the commit you want to undo was already pushed. This means that somebody else could have pulled this commit into her/his repository. If we undo this already publised commit, we are making everybody else's repositories obsolete! This can be indeed problematic depending on the number of users the project has, and their knowledge on Git to be able to solve this issue.
!!!! Undo a commit using ==git reset \-\-hard==
To undo the last commit, it is as easy as:
[[[language=bash
$ git reset --hard HEAD~1
]]]
==git reset \-\-hard [commitish]== makes your current branch point to [commitish]. ==HEAD== is your current head, and you can read ==~1== as \"minus one\". In other words, ==HEAD~1== is head minus one, which boils down to the parent of head, our previous commit.
You can use this same trick to rewrite the history in any other way, since you can use any commitish expression to reset. For example, ==HEAD~17== means 17 versions before head, or ==someBranch~4== means four commits before the branch ==someBranch==.
!!!! Update a commit's message using ==git commit \-\-amend==
To change our current commit's message you can use the following command:
[[[language=bash
$ git commit --amend -m "New commit message"
]]]
Or, if you don't use the ==\-m== option, a text editor will be prompt so you can edit a commit message.
[[[language=bash
$ git commit --amend
]]]
You can use the same trick not only to modify a commit's message but to modify your entire commit. Actually, just adding new things with ==git add== before an ==\-\-amend== will replace the current commit with a new commit merging the previous commit changes with what you just added.
!!!! Pushing rewritten history
As soon as the history we have rewritten was never pushed before, we can continue working normally and pushing our changes then without problems. However, if we have already pushed the commit we want to undo, this means that we are potentially impacting all users of our repository. Because of the problems it can pose to other people, pushing a rewritten history is not a completely favoured by Git. Better said, it is not allowed by default and you'll be warned about it:
[[[
$ git push
To git@github.com:REPOSITORY_OWNER/YOUR_REPOSITORY.git
! [rejected] YOUR_BRANCH -> YOUR_BRANCH (non-fast-forward)
error: failed to push some refs to 'git@github.com:REPOSITORY_OWNER/YOUR_REPOSITORY.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
]]]
With this message Git means that you should not blindly overwrite the history.
Also, it suggests to pull changes from the remote repository.
However, doing that will bring back to our repository the history we wanted to undo!
What we want to do is to impose our current (undone) state in the remote repository.
To do that, we need to ""force"" the push using the ==git push \-\-force== or the ==git push \-f== option.
[[[
$git push -f
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:REPOSITORY_OWNER/YOUR_REPOSITORY.git
+ a1713f3...6e0c7bf YOUR_BRANCH -> YOUR_BRANCH (forced update)
]]]