├── .gitignore
├── README.md
├── commit
├── git-config
├── git-find-repos
├── git-generate-changes
├── git-list-branches
├── git-practice-platform
├── git-wtf
├── gitanalyzer
├── gitcatfile
├── live-findfiles
├── live-git-index
├── live-git-log
├── live-git-reflog
├── live-git-status
└── live-tree
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | out/
3 | *.iml
4 | *.swp
5 | git-kata-directory/
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Git-Extras
3 |
4 | The best way of learning Git is practicing it. While practicing, it is beneficial to monitor the index, the status,
5 | the objects database and the working copy in some cases. A set of bash/groovy/ruby scripts are created to monitor
6 | and analyze Git internals and collected in this repository.
7 |
8 | ## Prerequisites
9 |
10 | The scrips in this repository are written in Bash, Groovy and Ruby and execute native Git commands. So in order to run
11 | all the scripts, the following applications should already be installed on your machine.
12 |
13 | * Git 1.7+
14 | * Groovy 2+
15 | * Ruby 2+
16 |
17 | ## Installation
18 |
19 | Just clone this repository to a folder (e.g. `/usr/local/bin`) which is in the list of `PATH` environment variable.
20 |
21 | ## Scripts
22 |
23 | **Bash Scripts**
24 | * [git-config-sample](#git-config-sample)
25 | * [git-find-repos](#git-find-repos)
26 | * [git-generate-changes](#git-generate-changes)
27 | * [git-practice-platform](#git-practice-platform)
28 |
29 | **Bash Scripts for Monitoring**
30 | * [live-findfiles](#live-findfiles)
31 | * [live-git-index](#live-git-index)
32 | * [live-git-log](#live-git-log)
33 | * [live-git-reflog](#live-git-reflog)
34 | * [live-git-status](#live-git-status)
35 | * [live-tree](#live-tree)
36 |
37 | **Groovy Scripts**
38 | * [gitanalyzer](#gitanalyzer)
39 | * [gitcatfile](#gitcatfile)
40 |
41 | **Ruby Scripts**
42 | * [git-wtf](#git-wtf)
43 |
44 | ## Usage
45 |
46 |
47 | #### git-config-sample
48 |
49 | ```
50 | NAME
51 | git-config-sample - Updates your git configuration with the ones we advice
52 |
53 | SYNOPSIS
54 | git-config-sample
55 |
56 | DESCRIPTION
57 | git-config-sample updates your git configuration with a set of config commands that we
58 | advice to use without fear. I also includes detailed explanation for each config entry.
59 |
60 | USAGE
61 | You can directly call the script and let it update the configuration.
62 |
63 | git-config-sample
64 |
65 | In order to check whether the configurations are updated or not, you can check
66 | `~/.gitconfig` file.
67 |
68 | DISCUSSIONS
69 | The content of this script is gathered after using Git for years with many tweaks.
70 | We also made a good search to learn how git-geeks use on their systems.
71 | ```
72 | [Go to top](#top)
73 |
74 |
75 | #### git-find-repos
76 |
77 | ```
78 | NAME
79 | git-find-repos - Finds all existing git repositories
80 |
81 | SYNOPSIS
82 | git-find-repos [-h | --help] [-d | --dirty]
83 |
84 | DESCRIPTION
85 | git-find-repos is a Bash script that searches .git folders recursively under the given folder
86 | path.
87 |
88 | OPTIONS
89 |
90 | Path of a folder to run the recursive search. If it is not provided, th directory that
91 | you are in is used for doing the search.
92 | -d, --dirty
93 | Checks the status of detected git repositories. Whenever it finds a repository having
94 | updates that hasn't been push to upstream (these are the updates not added yet, the
95 | updates added but not committed yet, and the updates committed but not pushed yet), it
96 | marks the repository as dirty. With this option, only the dirty repositories are printed,
97 | not all of them.
98 | -h, --help
99 | Prints usage information for help.
100 |
101 | USAGE
102 | Typically you can search for all available git repositories. Do not provide the path if you
103 | want to search in your current directory.
104 |
105 | git-find-repos
106 | git-find-repos /path/to/search
107 |
108 | In order to run the search just the dirty repositories, use -d option.
109 |
110 | git-find-repos -d
111 | git-find-repos -d /path/to/search
112 | git-find-repos --dirty /path/to/search
113 |
114 | DISCUSSIONS
115 | We clone git repositories to our machines. We use many different source directories to clone
116 | into. For instance, sometimes we clone repositories to our home folder, sometimes to our
117 | `/usr/local/` folder. From time to time we lose track of what we changed in these
118 | repositories. This script is mainly designed for searching git repositories having local
119 | modifications (i.e. -d option). It can also be used to list all git repositories in your
120 | machine to see the overall picture of what you cloned so far.
121 |
122 | It might be not possible to search git repositories in some directories due to missing access
123 | rights. On those directories, this script does not warn you.
124 | ```
125 | [Go to top](#top)
126 |
127 |
128 | #### git-generate-changes
129 |
130 | ```
131 | NAME
132 | git-generate-changes - Generates random file changes and commits to Git repository
133 |
134 | SYNOPSIS
135 | git-generate-changes [-h | --help] [-g | --commit] [(-p | --prefix) ]
136 | [(-c | --count) ] [(-e | --extension) ]
137 |
138 | DESCRIPTION
139 | git-generate-changes is a Bash script for creating bulk random commits in repository.
140 |
141 | OPTIONS
142 | -g, --commit
143 | Commit all newly created files to the repository as one file per commit.
144 | -p, --prefix
145 | The prefix of the filename used in the new files. If not given, "temp" is used.
146 | -c, --count
147 | Number of files to create. If not given, count is set to 1.
148 | -e, --extension
149 | The extension of the filename used in new files. If not given, "txt" is used.
150 | -h, --help
151 | Prints usage information for help.
152 |
153 | USAGE
154 | If no parameters are entered, the script creates 1 file with the default prefix and extension.
155 | The file is created in the root of the git repository. The number in filename is randomly
156 | selected between 1 and 100.
157 |
158 | $ git-generate-changes
159 | temp27.txt ... created
160 |
161 | You can define your own file name convention and decide to commit these files as one file
162 | per file.
163 |
164 | $ git-generate-changes -g -p catalina -e log -c 3
165 | catalina84.log ... created, committed
166 | catalina17.log ... created, committed
167 | catalina19.log ... created, committed
168 |
169 | If the random file name already exists in the same directory, the script updates it.
170 |
171 | $ git-generate-changes -g -p catalina -e log -c 3
172 | catalina63.log ... created, committed
173 | catalina17.log ... modified, committed
174 | catalina19.log ... modified, committed
175 |
176 | DISCUSSIONS
177 | This script is mainly used when you need random commits while practicing Git. It speeds up
178 | your exercise if your aim is trying -for instance- merge or rebase, not commit.
179 | ```
180 | [Go to top](#top)
181 |
182 |
183 | #### git-wtf
184 |
185 | ```
186 | NAME
187 | git-wtf - Displays the state of your repository in a readable, easy-to-scan format
188 |
189 | SYNOPSIS
190 | git-wtf [branch+] [options]
191 |
192 | DESCRIPTION
193 | git-wtf is a Ruby script. It displays the state of your repository in a readable, easy-to-scan
194 | format. It's useful for getting a summary of how a branch relates to a remote server, and for
195 | wrangling many topic branches.
196 |
197 | OPTIONS
198 | -l, --long
199 | include author info and date for each commit
200 | -a, --all
201 | show all branches across all remote repos, not just those from origin
202 | -A, --all-commits
203 | show all commits, not just the first 5
204 | -s, --short
205 | don't show commits
206 | -k, --key
207 | show key
208 | -r, --relations
209 | show relation to features / integration branches
210 | --dump-config
211 | print out current configuration and exit
212 |
213 | USAGE
214 | git-wtf uses some heuristics to determine which branches are integration branches, and which
215 | are feature branches. (Specifically, it assumes the integration branches are named "master",
216 | "next" and "edge".) If it guesses incorrectly, you will have to create a .git-wtfrc file.
217 |
218 | To start building a configuration file, run "git-wtf --dump-config > .git-wtfrc" and edit it.
219 | The config file is a YAML file that specifies the integration branches, any branches to
220 | ignore, and the max number of commits to display when --all-commits isn't used. git-wtf will
221 | look for a .git-wtfrc file starting in the current directory, and recursively up to the root.
222 |
223 | IMPORTANT NOTE: all local branches referenced in .git-wtfrc must be prefixed with heads/, e.g.
224 | "heads/master". Remote branches must be of the form remotes//.
225 |
226 | DISCUSSIONS
227 | This script is originally copyrighted to William Morgan .
228 | It is under terms of licensed in GNU General Public License.
229 | ```
230 | [Go to top](#top)
231 |
232 |
233 | #### gitanalyzer
234 |
235 | ```
236 | NAME
237 | gitanalyzer - Analyzes the index and object database of git repositories
238 |
239 | SYNOPSIS
240 | gitanalyzer
241 |
242 | DESCRIPTION
243 | gitanalyzer is a Groovy script using native Git commands. It analyzes the index (staging area)
244 | and the object database (i.e. .git/objects directory) and produces a report.
245 |
246 | The script checks all the objects in object database, compares them with the entries in index
247 | and detects the obsolete ones. As a result, the script creates a report listing all the blobs,
248 | trees, commits and tags with a mark implying the unreferenced objects.
249 |
250 | OPTIONS
251 | -d, --debug
252 | prints debug log information on the report
253 |
254 | USAGE
255 | The script should be called in a git repository.
256 |
257 | gitanalyzer
258 |
259 | The report displays all objects including files, folders, commits and tags as a list. At the
260 | end a summary is displayed.
261 |
262 | *** INDEX vs OBJECT DATABASE:
263 | index count : 117597
264 | object count : 117597
265 | obsolete count : 0
266 |
267 | *** OBJECT TYPES:
268 | commit : 5315
269 | tree : 89948
270 | blob : 22308
271 | tag : 26
272 |
273 | *** TIMINGS:
274 | execution time : 1185894.095 ms (20 min)
275 |
276 | DISCUSSIONS
277 | The initial aim of this script was to see the impact of garbage collection on objects. But
278 | then the report extended a little bit to prepare overall picture of the index and the object
279 | database.
280 |
281 | The script runs slow for big projects due to analysis per object. We plan to improve the
282 | performance in the coming releases.
283 | ```
284 | [Go to top](#top)
285 |
286 |
287 | #### git-practice-platform
288 |
289 | ```
290 | NAME
291 | git-practice-platform - Creates a bare repo and multiple users for practicing git
292 |
293 | SYNOPSIS
294 | git-practice-platform
295 | [-f | --folder ]
296 | [-r | --repository ]
297 | [-u | --usercount ]
298 |
299 | DESCRIPTION
300 | git-practice-platform is a Bash script that creates a bare repository for simulating remote
301 | repositories, multiple clients cloning the bare repository simulating a teamwork on the
302 | repository.
303 |
304 | OPTIONS
305 | -f | --folder
306 | The name of the root folder where all users clone the repository in it
307 | -r | --repository
308 | The name of the repository which is created for testing purposes
309 | -u | --usercount
310 | Number of cloning users. If not defined, 2 users are created by default.
311 | USAGE
312 | In order to create 3 users, the following command can be used.
313 |
314 | $ git-practice-platform -u 3 -f practicegit -r uberproject
315 | Root folder >>
316 | Created as /Users/user/practicegit
317 | Bare repository >>
318 | Created under server/uberproject.git
319 | User1 repository >>
320 | Created folder server/uberproject.git
321 | Cloned under clients/user1/uberproject.git
322 | Applied initial git configurations
323 | Created file .gitignore
324 | User2 repository >>
325 | Created folder server/uberproject.git
326 | Cloned under clients/user2/uberproject.git
327 | Applied initial git configurations
328 | Created file .gitignore
329 | User3 repository >>
330 | Created folder server/uberproject.git
331 | Cloned under clients/user3/uberproject.git
332 | Applied initial git configurations
333 | Created file .gitignore
334 | 1 bare repository and 3 clients are created. Ready for practicing.
335 |
336 | And the following folder structure is created.
337 |
338 | practicegit/
339 | |-- clients
340 | | |-- user1
341 | | | `-- uberproject.git
342 | | |-- user2
343 | | | `-- uberproject.git
344 | | `-- user3
345 | | `-- uberproject.git
346 | `-- server
347 | `-- uberproject.git
348 |
349 | DISCUSSIONS
350 | The best way of learning git is practicing it. We've been practicing git by creating bare
351 | and cloning repositories on our locals for a long time. This script automatises the whole
352 | create folders process and lets you initialize the practicing platform with one command.
353 | ```
354 | [Go to top](#top)
355 |
356 |
357 | #### live-findfiles
358 |
359 | ```
360 | NAME
361 | live-findfiles - Continuously displays the files with their paths as a list under the current
362 | folder
363 |
364 | SYNOPSIS
365 | live-findfiles []
366 |
367 | DESCRIPTION
368 | live-findfiles is a Bash script that runs find command on every second and displays the files
369 | with their paths (including hidden files and folders)
370 |
371 | USAGE
372 | The script runs `find` command and displays the content as a list. By default, it displays
373 | content of the current directory.
374 |
375 | live-findfiles
376 | live-findfiles .
377 |
378 | It also displays the content of any given directory continuously.
379 |
380 | live-findfiles /home/user/project/logs
381 | live-findfiles ~/data
382 | live-findfiles ../../items
383 |
384 | DISCUSSIONS
385 | The script can be used to monitor the contents of folders and visualize the impact of git
386 | commands. We use it to monitor object warehouse (.git/objects folder) while using git
387 | commands (add, commit and gc).
388 | ```
389 | [Go to top](#top)
390 |
391 |
392 | #### gitcatfile
393 |
394 | ```
395 | NAME
396 | gitcatfile - Displays the raw content of a git object
397 |
398 | SYNOPSIS
399 | gitcatfile [-e ]
400 |
401 | DESCRIPTION
402 | gitanalyzer is a Groovy script. It inflates git objects and displays the raw content. The
403 | objects could be any files under `.git/objects/` directory.
404 |
405 | OPTIONS
406 | -e, --extracts
407 | Extracts the object in the given path
408 |
409 | USAGE
410 | A valid git object file should be passed to display the raw content.
411 |
412 | $ gitcatfile -e .git/objects/1e/8a2b3dcc1092004df16fd151334a963815f4d5
413 | tree 38\0
414 | 100644 readme.txt\0 aba8d507fce75a7b9e7b98f800618695c483608b
415 |
416 | DISCUSSIONS
417 | Normally we can also view the content of each object by using `git cat-file -p` command.
418 | However it formats the output and does not let you see the raw content. This script
419 | inflates the file via ZLib inflator and parses the content byte by byte. Then converts it
420 | into human-friendly form.
421 |
422 | The following the the output of git cat-file command.
423 |
424 | $ git cat-file -p 1e8a2b
425 | 100644 blob aba8d507fce75a7b9e7b98f800618695c483608b readme.txt
426 |
427 | And the following is the output for the same object by our script.
428 |
429 | $ gitcatfile -e .git/objects/1e/8a2b3dcc1092004df16fd151334a963815f4d5
430 | tree 38\0
431 | 100644 readme.txt\0 aba8d507fce75a7b9e7b98f800618695c483608b
432 |
433 | We see null character as "\0" and 20 byte SHA-1 of referencing objects as hex string. New
434 | lines are added between entries to visualize better.
435 | ```
436 | [Go to top](#top)
437 |
438 |
439 | #### live-git-index
440 |
441 | ```
442 | NAME
443 | live-git-index - Generates random file changes and commits to Git repository
444 |
445 | SYNOPSIS
446 | live-git-index [-a]
447 |
448 | DESCRIPTION
449 | live-git-index is a Bash script for continuously displaying the content of the index.
450 |
451 | OPTIONS
452 | -a
453 | Displays the content stored in index for all references. The script displays the
454 | content for the current branch by default.
455 |
456 | USAGE
457 | If no parameters are given, the script displays the content of index for the current
458 | branch.
459 |
460 | $ live-git-index
461 | Current Branch with Staged Content:
462 | 100644 266df426af79b41503d47474e8d0703ed03cfe77 0 readme.txt
463 |
464 | In order to display all the content for all references, use the `-a` option.
465 |
466 | $ live-git-index
467 | Current Branch with Staged Content:
468 | 100644 266df426af79b41503d47474e8d0703ed03cfe77 0 readme.txt
469 |
470 | master:
471 | 100644 blob 266df426af79b41503d47474e8d0703ed03cfe77 readme.txt
472 |
473 | development:
474 | 100644 blob aba8d507fce75a7b9e7b98f800618695c483608b readme.txt
475 |
476 | test:
477 | 100644 blob 5b65f7b6a270e011bdbba46884b0d00a392e8e9f readme.txt
478 |
479 | In the sample above, 3 branches show 3 different version of the same `readme.txt` file.
480 |
481 | DISCUSSIONS
482 | This script can be used to display the content of the index and visualize the changes
483 | after each git command (i.e. add, commit, reset, etc.)
484 | ```
485 | [Go to top](#top)
486 |
487 |
488 | #### live-git-log
489 |
490 | ```
491 | NAME
492 | live-git-log - Continuously displays log graph
493 |
494 | SYNOPSIS
495 | live-git-log [-c | --count ]
496 |
497 | DESCRIPTION
498 | live-git-log is a Bash script that continuously displays the commit graph.
499 |
500 | OPTIONS
501 | -c, --count
502 | Max number of commits displayed in the graph is 20 by default. You can change the
503 | number by using `count` option.
504 |
505 | USAGE
506 | The script uses one-line format for `git log` command. It displays the logs in one of the
507 | most human-friendly format (i.e. colored, one line, showing graph)
508 |
509 | live-git-log
510 | live-git-log -c 10
511 |
512 | DISCUSSIONS
513 | This script is one of the most used script during git practices. With the help of this
514 | script, you can display the changes in the commit graph whenever it is applied.
515 | ```
516 | [Go to top](#top)
517 |
518 |
519 | #### live-git-reflog
520 |
521 | ```
522 | NAME
523 | live-git-reflog - Continuously displays log graph
524 |
525 | SYNOPSIS
526 | live-git-reflog [-c | --count ]
527 |
528 | DESCRIPTION
529 | live-git-reflog is a Bash script that continuously displays the entries in your reflog.
530 |
531 | OPTIONS
532 | -c, --count
533 | Max number of reflog entries displayed is 20 by default. You can change the
534 | number by using `count` option.
535 |
536 | USAGE
537 | The script uses one-line format for `git log` command. It displays the logs in one of the
538 | most human-friendly format (i.e. colored, one line, showing graph)
539 |
540 | live-git-log
541 | live-git-log -c 10
542 |
543 | DISCUSSIONS
544 | This script is one of the most used script during git practices. With the help of this
545 | script, you can display the changes in the commit graph whenever it is applied.
546 | ```
547 | [Go to top](#top)
548 |
549 |
550 | #### live-git-status
551 |
552 | ```
553 | NAME
554 | live-git-status - Continuously displays the status of git repository
555 |
556 | SYNOPSIS
557 | live-git-status
558 |
559 | DESCRIPTION
560 | live-git-status is a Bash script that continuously runs `git status` and displays the result.
561 |
562 | USAGE
563 | The script uses one-line short format for `git status` command to consume minimum space.
564 |
565 | live-git-status
566 |
567 | DISCUSSIONS
568 | This script is used to monitor status of git repository after your changes.
569 | ```
570 | [Go to top](#top)
571 |
572 |
573 | #### live-tree
574 |
575 | ```
576 | NAME
577 | live-tree - Continuously displays files and folders of the given path in a tree structure
578 |
579 | SYNOPSIS
580 | live-tree []
581 |
582 | DESCRIPTION
583 | live-tree is a Bash script that continuously displays files and folders of the given path
584 | in a tree structure. If path is not provided, the script uses the current path.
585 |
586 | OPTIONS
587 |
588 | Any directory to show the contents in tree format.
589 |
590 | USAGE
591 | The script uses `tree`command to build the tree structure. So if tree command is not
592 | installed in your system, you should install it. The script shows hidden files and folders
593 | too.
594 |
595 | live-tree
596 | live-tree /opt/data
597 |
598 | DISCUSSIONS
599 | This script is useful when continuously you want to display the contents of the folder having
600 | small number files and folders in it. If the contents is a lot, the terminal window might not
601 | display everything in one place.
602 | ```
603 | [Go to top](#top)
604 |
605 | ### License
606 |
607 | The programs in this repository is free software: you can redistribute it and/or modify it under the
608 | terms of the GNU General Public License as published by the Free Software Foundation, either version
609 | 3 of the License, or (at your option) any later version.
610 |
611 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
612 | the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
613 | License for more details.
614 |
615 | You can find the GNU General Public License at: [http://www.gnu.org/licenses](http://www.gnu.org/licenses)
616 |
--------------------------------------------------------------------------------
/commit:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | function getFileName() {
6 | randomNumber=$(( ( RANDOM % 100 ) + 1 ))
7 | file="file$randomNumber"
8 | echo $file
9 | }
10 |
11 | filename=$1
12 |
13 | if [ -z "$filename" ]; then
14 | filename=$(getFileName)
15 | fi
16 |
17 | if [ -f "$filename" ]; then
18 | echo "File $filename already exists"
19 | exit 0
20 | fi
21 |
22 | touch $filename
23 | git add $filename
24 | git commit -m "creates $filename"
25 |
--------------------------------------------------------------------------------
/git-config:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ######################
4 | # CONFIGURATIONS TO CHANGE
5 | ######################
6 |
7 | YOUR_NAME="name surname"
8 | YOUR_EMAIL="email@sample.com"
9 | PATH_TO_KDIFF3="/Applications/kdiff3.app/Contents/MacOS/kdiff3"
10 |
11 | ######################
12 | # This file contains useful git config commands with definitions
13 | # All configs are set globally in order to make it available for all your projects
14 | # Before running the commands, DO NOT FORGET to replace the words in curly braces with corresponding the data
15 | ######################
16 |
17 | ######################
18 | # INITIAL SETUP
19 | ######################
20 |
21 | # First config you have to do is to add your name and email to git
22 | git config --global user.name "${YOUR_NAME}"
23 | git config --global user.email "${YOUR_EMAIL}"
24 |
25 | ######################
26 | # ALIASES
27 | ######################
28 |
29 | # Shortcut for most used command as checkout
30 | git config --global alias.co "checkout"
31 |
32 | # Shortcut for most used command as status
33 | git config --global alias.s "status -uall"
34 |
35 | # Shows all branches including local and remote ones
36 | git config --global alias.b "branch -av"
37 |
38 | # Shows remote name - url mapping for fetch and push
39 | git config --global alias.r "remote -v"
40 |
41 | # Shows all existing tags
42 | git config --global alias.t "tag -l"
43 |
44 | # Combines git add all and git commit in one command
45 | git config --global alias.ac '!git add -A && git commit'
46 |
47 | # sychronize master branch without checking in it
48 | git config --global alias.master 'fetch origin master:master'
49 |
50 | # Shows diff between the version in working copy and the object database
51 | git config --global alias.d "difftool"
52 |
53 | # Shows diff between the version in working copy and staging area
54 | git config --global alias.dc "difftool --cached"
55 |
56 | # Shows diff between the version in working copy and staging area
57 | git config --global alias.dl "difftool HEAD..HEAD~"
58 |
59 | # FOR VERSIONS AFTER GIT v2.0
60 | # Shows all existing tags in a sorted list
61 | git config --global alias.t "tag -l --sort=version:refname"
62 |
63 | # Displays commit graph is a nice colored view for all branches including stashes
64 | # Use "-N" for displaying the last N commits
65 | git config --global alias.las "log --graph --all --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
66 |
67 | # Displays commit graph is a nice colored view for all branches
68 | git config --global alias.la "log --branches --remotes --tags --graph --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
69 |
70 | # Displays commit graph is a nice colored view for just the current branch
71 | # Use "-N" for displaying the last N commits
72 | git config --global alias.lb "log --graph --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
73 |
74 | # Displays each commit with full details one after another in a list view
75 | # Use "-N" for displaying the last N commits
76 | git config --global alias.lf "log --format=fuller"
77 |
78 | # Displays each commit as 1 line. It is mainly used to see commits in one shot.
79 | # Use "-N" for displaying the last N commits
80 | git config --global alias.l1 "log --stat --pretty=oneline"
81 |
82 | # Displays reflog data with the information of related commits
83 | git config --global alias.ref "log -g --pretty=format:'%C(yellow)%h%Creset %gD: %gs %Cred==>%Creset %C(yellow)%s%Creset (%cr) <%an>' --date=iso"
84 |
85 | # Removes the change set of your last commit from the commit store (your project's object database)
86 | # And keeps it in Index. The commit message of your last commit is also re-used in your next commit.
87 | git config --global alias.amend "commit --amend -C HEAD"
88 |
89 | # push new branch without setting a branch name to the upstream
90 | git config --global alias.pnb "push -u origin HEAD"
91 |
92 | # Displays the content of your given commit
93 | # Usage: git showco COMMIT_ID
94 | git config --global alias.showco "!git --no-pager show --stat --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
95 |
96 | # Removes all un-committed changes, including untracked files and the ones in Index
97 | git config --global alias.clear "!git reset --hard HEAD; git clean -f"
98 |
99 | # Removes all changesets from staging area
100 | git config --global alias.unstage "reset HEAD"
101 |
102 | # Shows commit messages one line per commit between 2 commits
103 | # Use like "git changelog v01..HEAD"
104 | git config --global alias.changelog "log --pretty=format:' * %s'"
105 |
106 | # Displays last update date for all existing tags and branches in a graph view
107 | # Then you can learn all old legacy branches and tags from old days
108 | git config --global alias.dates "log --date-order --graph --tags --branches --simplify-by-decoration --pretty=format:'%ai %h %d'"
109 |
110 | # Forces to run garbage collector to remove unreferenced objects and pack the others
111 | git config --global alias.gc-all "-c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc --force"
112 |
113 | ######################
114 | # CORE
115 | ######################
116 |
117 | # Git does not mark your code if the line endings are changed
118 | git config --global core.autocrlf "false"
119 |
120 | # Sets the default editor for writing commit messages or editing interactive rebase commands to vim
121 | git config --global core.editor "vim"
122 |
123 | # Change the default pager application to 'less'
124 | git config --global --replace-all core.pager "less -iXFR"
125 |
126 |
127 | ######################
128 | # PUSH
129 | ######################
130 |
131 | # FOR VERSIONS BEFORE GIT v2.0
132 | # Before git v2, push command sends all your un-pushed commits regardless of checking the source branch
133 | # It means you will push commits from other branches if you don't specify branch like "git push origin BRANCH_NAME"
134 | # By changing the default behavior to simple, you guarantee to push the commits of your current branch only
135 | git config --global push.default "simple"
136 |
137 | ######################
138 | # BRANCH
139 | ######################
140 |
141 | # Pull will always do fetch + rebase instead of fetch + merge as default behavior
142 | git config --global branch.autosetuprebase "always"
143 |
144 | # Whenever you create a branch via "branch" or "checkout -b" commands, git will automatically track the remote one
145 | git config --global branch.autosetupmerge "always"
146 |
147 | ######################
148 | # LOG
149 | ######################
150 |
151 | # Print out the ref names of any commits that are shown. The ref name prefixes refs/heads/, refs/tags/ and refs/remotes/ will be printed.
152 | git config --global log.decorate "full"
153 |
154 | # Shows timestamps in ISO 8601 format: http://en.wikipedia.org/wiki/ISO_8601
155 | git config --global log.date "iso"
156 |
157 | ######################
158 | # COLOR
159 | ######################
160 |
161 | # Prints all outputs in color whenever possible
162 | git config --global color.diff "auto"
163 | git config --global color.status "auto"
164 | git config --global color.branch "auto"
165 | git config --global color.interactive "auto"
166 | git config --global color.ui "auto"
167 |
168 | ######################
169 | # DIFF
170 | ######################
171 |
172 | # Changes the diff algorithm to a more efficient one
173 | git config --global diff.algorithm "patience"
174 |
175 | # Tell git diff to use mnemonic prefixes (index, work tree, commit, object) instead of the standard a and b notation
176 | git config --global diff.mnemonicprefix "true"
177 |
178 | # Allow git diff to do basic rename and copy detection
179 | git config --global diff.renames "copies"
180 |
181 | ######################
182 | # DIFFTOOL
183 | ######################
184 |
185 | # Using kdiff3 for resolving for diffs.
186 | git config --global difftool.kdiff3.path "${PATH_TO_KDIFF3}"
187 | git config --global difftool.kdiff3.trustExitCode "true"
188 | git config --global difftool.prompt "false"
189 | git config --global diff.tool "kdiff3"
190 |
191 | ######################
192 | # MERGE
193 | ######################
194 |
195 | # Puts the commit messages of the ones that are merged to the commit message of the merge
196 | git config --global merge.summary "true"
197 |
198 | # Always show a diffstat at the end of a merge
199 | git config --global merge.stat "true"
200 |
201 | ######################
202 | # MERGETOOL
203 | ######################
204 |
205 | # Using kdiff3 for resolving for resolving merge conflicts.
206 | git config --global mergetool.kdiff3.path "{PATH_TO_KDIFF3}"
207 | git config --global mergetool.kdiff3.trustExitCode "true"
208 | git config --global mergetool.keepBackup "false"
209 | git config --global merge.tool "kdiff3"
210 |
211 | ######################
212 | # CREDENTIALS
213 | ######################
214 |
215 | # if you're on Mac OS and used homebrew to install git, you can use the native Mac OS keystore
216 | # Then there will be no need to enter your passphrase on every pull & push
217 | git config --global credential.helper "osxkeychain"
218 |
--------------------------------------------------------------------------------
/git-find-repos:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | usage() {
6 | echo "USAGE: git-find-repos [-h | --help] [-d | --dirty] "
7 | exit 1
8 | }
9 |
10 | dirty=
11 | path=$(pwd)
12 |
13 | realargs="$@"
14 | while [ $# -gt 0 ]; do
15 | case "$1" in
16 | -h | --help)
17 | usage
18 | ;;
19 | -d | --dirty)
20 | dirty=1
21 | ;;
22 | *)
23 | path=$1
24 | ;;
25 | esac
26 | shift
27 | done
28 | set -- $realargs
29 |
30 | COUNTER=0
31 | DIRTYCOUNTER=0
32 |
33 | start_time=`date +%s`
34 |
35 | if [ -n "$dirty" ]; then
36 | while IFS= read -r -d $'\0' line; do
37 | COUNTER=$(( $COUNTER + 1 ))
38 | tmpPath=$(dirname ${line})
39 |
40 | cd ${tmpPath}
41 | gitStatus=$(git status -uall -s 2>/dev/null)
42 | gitUnpushedCommits=$(git --no-pager log --graph --branches --not --remotes --oneline 2>/dev/null)
43 |
44 | if [ -n "$gitStatus" ] || [ -n "$gitUnpushedCommits" ]; then
45 | DIRTYCOUNTER=$(( $DIRTYCOUNTER + 1 ))
46 | echo "[$DIRTYCOUNTER.] $tmpPath"
47 | fi
48 | done < <(find $path -type d -name ".git" -print0 2>/dev/null)
49 | echo "Found $DIRTYCOUNTER changed repos among $COUNTER git repositories under $path in $(expr `date +%s` - $start_time) seconds"
50 | else
51 | while IFS= read -r -d $'\0' line; do
52 | COUNTER=$(( $COUNTER + 1 ))
53 | echo "[$COUNTER.] ${line}"
54 | done < <(find $path -type d -name ".git" -print0 2>/dev/null)
55 | echo "Found $COUNTER git repositories under $path in $(expr `date +%s` - $start_time) seconds"
56 | fi
57 |
58 |
--------------------------------------------------------------------------------
/git-generate-changes:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | fileNameNumberUpperLimit=100
6 | commentNumberUpperLimit=1000
7 |
8 | function getFileName() {
9 | randomNumber=$(( ( RANDOM % $fileNameNumberUpperLimit ) + 1 ))
10 | file="$1$randomNumber.$2"
11 | echo $file
12 | }
13 |
14 | function create() {
15 | if [ -f $1 ];
16 | then
17 | echo "Modified file $randomNumber" >> $1
18 | echo -n " ... modified"
19 | else
20 | echo "Created file $randomNumber" >> $1
21 | echo -n " ... created"
22 | fi
23 | }
24 |
25 | function isInGitRepo() {
26 | git rev-parse --is-inside-work-tree 2>/dev/null
27 | statusCode=$?
28 | if [ $statusCode -eq 0 ]; then
29 | echo 1
30 | else
31 | echo 0
32 | fi
33 | }
34 |
35 | function commitToGitRepo() {
36 | if [ -n "$1" ]; then
37 | git add -A
38 | randomNumber=$(( ( RANDOM % $commentNumberUpperLimit ) + 1 ))
39 | git commit -m "Random changes #${randomNumber}" >/dev/null 2>&1
40 | echo -n ", committed"
41 | fi
42 | }
43 |
44 | usage() { echo "USAGE: git-generate-changes -g -p -c -e "; exit 1; }
45 |
46 | prefix="temp"
47 | count=1
48 | extension="txt"
49 | togit=
50 |
51 | realargs="$@"
52 | while [ $# -gt 0 ]; do
53 | case "$1" in
54 | -h | --help)
55 | usage
56 | ;;
57 | -p | --prefix)
58 | prefix=$2
59 | shift
60 | ;;
61 | -c | --count)
62 | count=$2
63 | shift
64 | ;;
65 | -e | --extension)
66 | extension=$2
67 | shift
68 | ;;
69 | -g | --commit)
70 | togit=1
71 | ;;
72 | *)
73 | usage
74 | break
75 | ;;
76 | esac
77 | shift
78 | done
79 | set -- $realargs
80 |
81 | gitStatus=$(isInGitRepo)
82 | if [ -n "$togit" ] && [ "$gitStatus" == true ]; then
83 | echo "FATAL: The current directory is not in a git repository"
84 | exit 0
85 | fi
86 |
87 | counter=1;
88 | while [[ $counter -le "$count" ]];
89 | do
90 | file=$(getFileName $prefix $extension)
91 | echo -n "$file"
92 | create $file
93 | commitToGitRepo $togit
94 | echo ""
95 | let "counter += 1";
96 | done
97 |
98 |
99 |
--------------------------------------------------------------------------------
/git-list-branches:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo ""
3 | echo -e "###> \033[31mMANDATORY BRANCHES\033[0m"
4 | echo ""
5 | echo -e " \033[32mmaster\033[0m and \033[32mdevelopment\033[0m branches are excluded from the list"
6 |
7 | count=$(git branch -r --no-merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1 | wc -l)
8 | echo ""
9 | echo -e "###> \033[31m${count//[[:space:]]/} UNMERGED REMOTE BRANCHES\033[0m"
10 | echo ""
11 |
12 | git branch -r --no-merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1
13 |
14 | count=$(git branch --no-merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1 | wc -l)
15 | echo ""
16 | echo -e "###> \033[31m${count//[[:space:]]/} UNMERGED LOCAL BRANCHES\033[0m"
17 | echo ""
18 |
19 | git branch --no-merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1
20 |
21 | count=$(git branch -r --merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1 | wc -l)
22 | echo ""
23 | echo -e "###> \033[31m${count//[[:space:]]/} MERGED REMOTE BRANCHES\033[0m"
24 | echo ""
25 |
26 | git branch -r --merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1
27 |
28 | count=$(git branch --merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1 | wc -l)
29 | echo ""
30 | echo -e "###> \033[31m${count//[[:space:]]/} MERGED LOCAL BRANCHES\033[0m"
31 | echo ""
32 |
33 | git branch --merged | grep -v master | grep -v development | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1
34 |
35 | echo ""
36 |
--------------------------------------------------------------------------------
/git-practice-platform:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | usage() {
6 | echo "USAGE: git-practice-platform [-f | --folder ] [-r | --repository ] [-u | --usercount ]"
7 | exit 1
8 | }
9 |
10 | repo=
11 | folder=
12 | user_count=2
13 | path=$(pwd)
14 |
15 | realargs="$@"
16 | while [ $# -gt 0 ]; do
17 | case "$1" in
18 | -r | --repository)
19 | repo=$2
20 | shift
21 | ;;
22 | -f | --folder)
23 | folder=$2
24 | shift
25 | ;;
26 | -u | --usercount)
27 | user_count=$2
28 | shift
29 | ;;
30 | *)
31 | usage
32 | ;;
33 | esac
34 | shift
35 | done
36 | set -- $realargs
37 |
38 | if [ -z "$repo" ] || [ -z "$folder" ]; then
39 | usage
40 | return
41 | fi
42 |
43 | if [ -d "$path/$folder" ]; then
44 | echo "Folder already exists in $path/$folder"
45 | echo "Quited"
46 | exit 0
47 | fi
48 |
49 | root=${path}/${folder}
50 | mkdir -p ${root}
51 | echo "Root folder >>"
52 | echo " Created as ${root}"
53 |
54 | mkdir -p ${root}/server/${repo}.git > /dev/null 2>&1
55 | cd ${root}/server/${repo}.git
56 | git init --bare > /dev/null 2>&1
57 | echo "Bare repository >>"
58 | echo " Created under server/${repo}.git"
59 |
60 | for (( i=1; i<=${user_count}; i++ ))
61 | do
62 | echo "User${i} repository >>"
63 |
64 | mkdir -p ${root}/clients > /dev/null 2>&1
65 | mkdir -p ${root}/clients/user${i} > /dev/null 2>&1
66 | echo " Created folder server/${repo}.git"
67 |
68 | cd ${root}/clients/user${i}
69 | git clone "file://${root}/server/${repo}.git" ${repo}.git > /dev/null 2>&1
70 | echo " Cloned under clients/user${i}/${repo}.git"
71 |
72 | cd ${root}/clients/user${i}/${repo}.git
73 | git config user.name "User${i}"
74 | git config user.email "user${i}@gittraining.com"
75 | git config branch.autosetuprebase never
76 | git config branch.autosetupmerge true
77 | git config branch.master.rebase false
78 | git config alias.la "log --graph --all --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
79 | git config alias.lb "log --graph --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
80 | git config alias.lf "log --format=fuller"
81 | git config alias.l1 "log --stat --pretty=oneline"
82 |
83 | echo " Applied initial git configurations"
84 |
85 | done
86 |
87 | echo "1 bare repository and ${user_count} clients are created. Ready for practicing."
88 |
--------------------------------------------------------------------------------
/git-wtf:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | HELP = <
54 | .git-wtfrc" and edit it. The config file is a YAML file that specifies the
55 | integration branches, any branches to ignore, and the max number of commits to
56 | display when --all-commits isn't used. git-wtf will look for a .git-wtfrc file
57 | starting in the current directory, and recursively up to the root.
58 |
59 | IMPORTANT NOTE: all local branches referenced in .git-wtfrc must be prefixed
60 | with heads/, e.g. "heads/master". Remote branches must be of the form
61 | remotes//.
62 | EOS
63 |
64 | COPYRIGHT = <.
66 | This program is free software: you can redistribute it and/or modify it
67 | under the terms of the GNU General Public License as published by the Free
68 | Software Foundation, either version 3 of the License, or (at your option)
69 | any later version.
70 |
71 | This program is distributed in the hope that it will be useful, but WITHOUT
72 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
73 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
74 | more details.
75 |
76 | You can find the GNU General Public License at: http://www.gnu.org/licenses/
77 | EOS
78 |
79 | require 'yaml'
80 | CONFIG_FN = ".git-wtfrc"
81 |
82 | class Numeric; def pluralize s; "#{to_s} #{s}" + (self != 1 ? "s" : "") end end
83 |
84 | if ARGV.delete("--help") || ARGV.delete("-h")
85 | puts USAGE
86 | exit
87 | end
88 |
89 | ## poor man's trollop
90 | $long = ARGV.delete("--long") || ARGV.delete("-l")
91 | $short = ARGV.delete("--short") || ARGV.delete("-s")
92 | $all = ARGV.delete("--all") || ARGV.delete("-a")
93 | $all_commits = ARGV.delete("--all-commits") || ARGV.delete("-A")
94 | $dump_config = ARGV.delete("--dump-config")
95 | $key = ARGV.delete("--key") || ARGV.delete("-k")
96 | $show_relations = ARGV.delete("--relations") || ARGV.delete("-r")
97 | ARGV.each { |a| abort "Error: unknown argument #{a}." if a =~ /^--/ }
98 |
99 | ## search up the path for a file
100 | def find_file fn
101 | while true
102 | return fn if File.exist? fn
103 | fn2 = File.join("..", fn)
104 | return nil if File.expand_path(fn2) == File.expand_path(fn)
105 | fn = fn2
106 | end
107 | end
108 |
109 | want_color = `git config color.wtf`
110 | want_color = `git config color.ui` if want_color.empty?
111 | $color = case want_color.chomp
112 | when "true"; true
113 | when "auto"; $stdout.tty?
114 | end
115 |
116 | def red s; $color ? "\033[31m#{s}\033[0m" : s end
117 | def green s; $color ? "\033[32m#{s}\033[0m" : s end
118 | def yellow s; $color ? "\033[33m#{s}\033[0m" : s end
119 | def cyan s; $color ? "\033[36m#{s}\033[0m" : s end
120 | def grey s; $color ? "\033[1;30m#{s}\033[0m" : s end
121 | def purple s; $color ? "\033[35m#{s}\033[0m" : s end
122 |
123 | ## the set of commits in 'to' that aren't in 'from'.
124 | ## if empty, 'to' has been merged into 'from'.
125 | def commits_between from, to
126 | if $long
127 | `git log --pretty=format:"- %s [#{yellow "%h"}] (#{purple "%ae"}; %ar)" #{from}..#{to}`
128 | else
129 | `git log --pretty=format:"- %s [#{yellow "%h"}]" #{from}..#{to}`
130 | end.split(/[\r\n]+/)
131 | end
132 |
133 | def show_commits commits, prefix=" "
134 | if commits.empty?
135 | puts "#{prefix} none"
136 | else
137 | max = $all_commits ? commits.size : $config["max_commits"]
138 | max -= 1 if max == commits.size - 1 # never show "and 1 more"
139 | commits[0 ... max].each { |c| puts "#{prefix}#{c}" }
140 | puts grey("#{prefix}... and #{commits.size - max} more (use -A to see all).") if commits.size > max
141 | end
142 | end
143 |
144 | def ahead_behind_string ahead, behind
145 | [ahead.empty? ? nil : "#{ahead.size.pluralize 'commit'} ahead",
146 | behind.empty? ? nil : "#{behind.size.pluralize 'commit'} behind"].
147 | compact.join("; ")
148 | end
149 |
150 | def widget merged_in, remote_only=false, local_only=false, local_only_merge=false
151 | left, right = case
152 | when remote_only; %w({ })
153 | when local_only; %w{( )}
154 | else %w([ ])
155 | end
156 | middle = case
157 | when merged_in && local_only_merge; green("~")
158 | when merged_in; green("x")
159 | else " "
160 | end
161 | print left, middle, right
162 | end
163 |
164 | def show b
165 | have_both = b[:local_branch] && b[:remote_branch]
166 |
167 | pushc, pullc, oosync = if have_both
168 | [x = commits_between(b[:remote_branch], b[:local_branch]),
169 | y = commits_between(b[:local_branch], b[:remote_branch]),
170 | !x.empty? && !y.empty?]
171 | end
172 |
173 | if b[:local_branch]
174 | puts "Local branch: " + green(b[:local_branch].sub(/^heads\//, ""))
175 |
176 | if have_both
177 | if pushc.empty?
178 | puts "#{widget true} in sync with remote"
179 | else
180 | action = oosync ? "push after rebase / merge" : "push"
181 | puts "#{widget false} NOT in sync with remote (you should #{action})"
182 | show_commits pushc unless $short
183 | end
184 | end
185 | end
186 |
187 | if b[:remote_branch]
188 | puts "Remote branch: #{cyan b[:remote_branch]} (#{b[:remote_url]})"
189 |
190 | if have_both
191 | if pullc.empty?
192 | puts "#{widget true} in sync with local"
193 | else
194 | action = pushc.empty? ? "merge" : "rebase / merge"
195 | puts "#{widget false} NOT in sync with local (you should #{action})"
196 | show_commits pullc unless $short
197 | end
198 | end
199 | end
200 |
201 | puts "\n#{red "WARNING"}: local and remote branches have diverged. A merge will occur unless you rebase." if oosync
202 | end
203 |
204 | def show_relations b, all_branches
205 | ibs, fbs = all_branches.partition { |name, br| $config["integration-branches"].include?(br[:local_branch]) || $config["integration-branches"].include?(br[:remote_branch]) }
206 | if $config["integration-branches"].include? b[:local_branch]
207 | puts "\nFeature branches:" unless fbs.empty?
208 | fbs.each do |name, br|
209 | next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch])
210 | next if br[:ignore]
211 | local_only = br[:remote_branch].nil?
212 | remote_only = br[:local_branch].nil?
213 | name = if local_only
214 | purple br[:name]
215 | elsif remote_only
216 | cyan br[:name]
217 | else
218 | green br[:name]
219 | end
220 |
221 | ## for remote_only branches, we'll compute wrt the remote branch head. otherwise, we'll
222 | ## use the local branch head.
223 | head = remote_only ? br[:remote_branch] : br[:local_branch]
224 |
225 | remote_ahead = b[:remote_branch] ? commits_between(b[:remote_branch], head) : []
226 | local_ahead = b[:local_branch] ? commits_between(b[:local_branch], head) : []
227 |
228 | if local_ahead.empty? && remote_ahead.empty?
229 | puts "#{widget true, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is merged in"
230 | elsif local_ahead.empty?
231 | puts "#{widget true, remote_only, local_only, true} #{name} merged in (only locally)"
232 | else
233 | behind = commits_between head, (br[:local_branch] || br[:remote_branch])
234 | ahead = remote_only ? remote_ahead : local_ahead
235 | puts "#{widget false, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is NOT merged in (#{ahead_behind_string ahead, behind})"
236 | show_commits ahead unless $short
237 | end
238 | end
239 | else
240 | puts "\nIntegration branches:" unless ibs.empty? # unlikely
241 | ibs.sort_by { |v, br| v }.each do |v, br|
242 | next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch])
243 | next if br[:ignore]
244 | local_only = br[:remote_branch].nil?
245 | remote_only = br[:local_branch].nil?
246 | name = remote_only ? cyan(br[:name]) : green(br[:name])
247 |
248 | ahead = commits_between v, (b[:local_branch] || b[:remote_branch])
249 | if ahead.empty?
250 | puts "#{widget true, local_only} merged into #{name}"
251 | else
252 | #behind = commits_between b[:local_branch], v
253 | puts "#{widget false, local_only} NOT merged into #{name} (#{ahead.size.pluralize 'commit'} ahead)"
254 | show_commits ahead unless $short
255 | end
256 | end
257 | end
258 | end
259 |
260 | #### EXECUTION STARTS HERE ####
261 |
262 | ## find config file and load it
263 | $config = { "integration-branches" => %w(heads/master heads/next heads/edge), "ignore" => [], "max_commits" => 5 }.merge begin
264 | fn = find_file CONFIG_FN
265 | if fn && (h = YAML::load_file(fn)) # yaml turns empty files into false
266 | h["integration-branches"] ||= h["versions"] # support old nomenclature
267 | h
268 | else
269 | {}
270 | end
271 | end
272 |
273 | if $dump_config
274 | puts $config.to_yaml
275 | exit
276 | end
277 |
278 | ## first, index registered remotes
279 | remotes = `git config --get-regexp ^remote\.\*\.url`.split(/[\r\n]+/).inject({}) do |hash, l|
280 | l =~ /^remote\.(.+?)\.url (.+)$/ or next hash
281 | hash[$1] ||= $2
282 | hash
283 | end
284 |
285 | ## next, index followed branches
286 | branches = `git config --get-regexp ^branch\.`.split(/[\r\n]+/).inject({}) do |hash, l|
287 | case l
288 | when /branch\.(.*?)\.remote (.+)/
289 | name, remote = $1, $2
290 |
291 | hash[name] ||= {}
292 | hash[name].merge! :remote => remote, :remote_url => remotes[remote]
293 | when /branch\.(.*?)\.merge ((refs\/)?heads\/)?(.+)/
294 | name, remote_branch = $1, $4
295 | hash[name] ||= {}
296 | hash[name].merge! :remote_mergepoint => remote_branch
297 | end
298 | hash
299 | end
300 |
301 | ## finally, index all branches
302 | remote_branches = {}
303 | `git show-ref`.split(/[\r\n]+/).each do |l|
304 | sha1, ref = l.chomp.split " refs/"
305 |
306 | if ref =~ /^heads\/(.+)$/ # local branch
307 | name = $1
308 | next if name == "HEAD"
309 | branches[name] ||= {}
310 | branches[name].merge! :name => name, :local_branch => ref
311 | elsif ref =~ /^remotes\/(.+?)\/(.+)$/ # remote branch
312 | remote, name = $1, $2
313 | remote_branches["#{remote}/#{name}"] = true
314 | next if name == "HEAD"
315 | ignore = !($all || remote == "origin")
316 |
317 | branch = name
318 | if branches[name] && branches[name][:remote] == remote
319 | # nothing
320 | else
321 | name = "#{remote}/#{branch}"
322 | end
323 |
324 | branches[name] ||= {}
325 | branches[name].merge! :name => name, :remote => remote, :remote_branch => "#{remote}/#{branch}", :remote_url => remotes[remote], :ignore => ignore
326 | end
327 | end
328 |
329 | ## assemble remotes
330 | branches.each do |k, b|
331 | next unless b[:remote] && b[:remote_mergepoint]
332 | b[:remote_branch] = if b[:remote] == "."
333 | b[:remote_mergepoint]
334 | else
335 | t = "#{b[:remote]}/#{b[:remote_mergepoint]}"
336 | remote_branches[t] && t # only if it's still alive
337 | end
338 | end
339 |
340 | show_dirty = ARGV.empty?
341 | targets = if ARGV.empty?
342 | [`git symbolic-ref HEAD`.chomp.sub(/^refs\/heads\//, "")]
343 | else
344 | ARGV.map { |x| x.sub(/^heads\//, "") }
345 | end.map { |t| branches[t] or abort "Error: can't find branch #{t.inspect}." }
346 |
347 | targets.each do |t|
348 | show t
349 | show_relations t, branches if $show_relations || t[:remote_branch].nil?
350 | end
351 |
352 | modified = show_dirty && `git ls-files -m` != ""
353 | uncommitted = show_dirty && `git diff-index --cached HEAD` != ""
354 |
355 | if $key
356 | puts
357 | puts KEY
358 | end
359 |
360 | puts if modified || uncommitted
361 | puts "#{red "NOTE"}: working directory contains modified files." if modified
362 | puts "#{red "NOTE"}: staging area contains staged but uncommitted files." if uncommitted
363 |
364 | # the end!
365 |
366 |
--------------------------------------------------------------------------------
/gitanalyzer:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env groovy
2 |
3 | import java.text.SimpleDateFormat
4 |
5 | path = new File(".").getAbsolutePath()
6 | isDebug = false
7 |
8 | def cli = new CliBuilder(usage: 'GitAnalyzer.groovy [-p PATH] [-d]')
9 | detectArguments(cli, args)
10 |
11 | stats = [:]
12 | root = new File(path)
13 | validTypes = ["blob", "tree", "commit", "tag"]
14 |
15 | // ============================
16 | // CHECKING VALID GIT REPO
17 | // ============================
18 |
19 | log("\n***** ANALYSIS *****\n\n")
20 | assertGitRepo(cli, path)
21 |
22 | // ============================
23 | // UNPACKING OBJECTS
24 | // ============================
25 |
26 | log("Unpacking any packed objects ... ")
27 |
28 | def packsDir = new File(path + "/.git/objects/pack")
29 | def newPackDir = new File(path + "/pack_" + UUID.randomUUID().toString())
30 | packsDir.renameTo(newPackDir)
31 | runBash("git unpack-objects < ${newPackDir.name}/*.pack")
32 | newPackDir.deleteDir()
33 |
34 | log("OK")
35 |
36 | // ============================
37 | // ANALYZE
38 | // ============================
39 |
40 | analyze()
41 | display()
42 |
43 | // ============================
44 | // METHODS
45 | // ============================
46 |
47 | def analyze() {
48 | execStartTime = System.nanoTime()
49 |
50 | // CHECKING INDEX
51 |
52 | revsMap = [:]
53 |
54 | log("Checking all revisions of all objects in index ... ")
55 |
56 | "git rev-list --objects --all".execute(null, root).text.eachLine {
57 | def tokens = it.tokenize()
58 | revsMap[tokens[0]] = tokens[1]
59 | count("index count")
60 | }
61 | log("OK")
62 |
63 | // ANALYZING OBJECTS DATABASE
64 |
65 | def objectsDir = new File(path + "/.git/objects")
66 | objectsMap = [:]
67 |
68 | log("Analyzing each object in object warehouse and index ... ")
69 |
70 | objectsDir.eachDir { dir ->
71 | dir.eachFile { file ->
72 | def id = dir.name + file.name
73 | objectsMap[id] = [:]
74 |
75 | def type = "git cat-file $id -t".execute(null, root).text.replace("\n", "")
76 | objectsMap[id]["type"] = type
77 | objectsMap[id]["filePath"] = (
78 | revsMap[id] ?: (type == "tree" ? "[ROOT]" : "")
79 | )
80 | objectsMap[id]["id"] = id
81 |
82 | if (type == "commit") {
83 | def commitData = runBash("git show -s --format=\"%ci|%cn|%s\" $id").text.replace("\n", " ").trim()
84 | def commitToken = commitData.tokenize("|")
85 | objectsMap[id]["commitDate"] = commitToken[0]
86 | objectsMap[id]["committer"] = commitToken[1]
87 | objectsMap[id]["commitMessage"] = commitToken[2]
88 | }
89 |
90 | if (type && !revsMap.containsKey(id)) {
91 | objectsMap[id]["obsolete"] = true
92 | count("obsolete")
93 | }
94 |
95 | if (validTypes.contains(type)) {
96 | count(type)
97 | count("object count")
98 | }
99 | }
100 | }
101 | log("OK")
102 | }
103 |
104 | def display() {
105 | // DISPLAY COMMITS
106 |
107 | println "\n***** COMMITS *****\n"
108 | objectsMap.sort { it.value.commitDate }.each { String id, vals ->
109 | if (vals.type == "commit") {
110 | printf "%-3s %.6s => [%s] %20s --- %s\n", markObsoletes(vals), id.substring(0, 8), format(vals.commitDate), vals.committer, vals.commitMessage
111 | }
112 | }
113 |
114 | // DISPLAY OBJECTS
115 |
116 | println "\n***** TREES - BLOBS - TAGS *****\n"
117 | objectsMap.sort { it.value.filePath }.each { String id, vals ->
118 | if (vals.type != "commit" && !vals.id.startsWith("infopa")) {
119 | printf "%-3s %.6s => %-8s %s\n", markObsoletes(vals), id.substring(0, 8), vals.type, vals.filePath
120 | }
121 | }
122 |
123 | // DISPLAY STATS
124 |
125 | println "\n***** STATS *****"
126 | stats["execution time"] = "${((double) System.nanoTime() - execStartTime) / 1000000.0} ms"
127 | println "\n*** INDEX vs OBJECT DATABASE:"
128 | printf "%-15s : %s\n", "index count", stats["index count"] ?: 0
129 | printf "%-15s : %s\n", "object count", stats["object count"] ?: 0
130 | printf "%-15s : %s\n", "obsolete count", stats["obsolete"] ?: 0
131 | println "\n*** OBJECT TYPES:"
132 | printf "%-15s : %s\n", "commit", stats["commit"] ?: 0
133 | printf "%-15s : %s\n", "tree", stats["tree"] ?: 0
134 | printf "%-15s : %s\n", "blob", stats["blob"] ?: 0
135 | printf "%-15s : %s\n", "tag", stats["tag"] ?: 0
136 | println "\n*** TIMINGS:"
137 | printf "%-15s : %s\n", "execution time", stats["execution time"]
138 | }
139 |
140 | def runBash(cmd) {
141 | def env = System.getenv();
142 | def envList = [];
143 | env.each() { k, v -> envList.push("$k=$v") }
144 | proc = ["bash", "-c", cmd].execute(envList, root);
145 | proc.waitFor()
146 | return proc
147 | }
148 |
149 | def markObsoletes(vals) {
150 | if (vals.obsolete) return "* "
151 | return ""
152 | }
153 |
154 | def count(name) {
155 | stats["$name"] = (stats["$name"] ?: 0) + 1
156 | }
157 |
158 | def format(val) {
159 | if (val instanceof Date) {
160 | def format = new SimpleDateFormat("yyyy-MM-dd HH:mm")
161 | return format.format(val);
162 | }
163 | return val
164 | }
165 |
166 | def detectArguments(cli, args) {
167 | cli.with {
168 | h longOpt: 'help', 'Show usage information'
169 | p longOpt: 'path', args: 1, argName: 'path', 'Analyzes internal structure of git repository in "path"'
170 | d longOpt: 'isDebug', 'Show debug information'
171 | }
172 |
173 | def options = cli.parse(args)
174 | if (!options) {
175 | return
176 | }
177 | if (options.h) {
178 | cli.usage()
179 | return
180 | }
181 | if (options.path) {
182 | path = options.path
183 | }
184 | if (options.isDebug) {
185 | isDebug = true
186 | }
187 | }
188 |
189 | def assertGitRepo(cli, pathOfRepo) {
190 | log("Checking if the given path is a valid git repository ... ")
191 | if (!new File(pathOfRepo + "/.git").exists()) {
192 | println "[ERROR] $pathOfRepo is not a valid git repository"
193 | cli.usage()
194 | System.exit(0)
195 | }
196 | log("OK")
197 | }
198 |
199 | startTime = 0
200 |
201 | def log(msg) {
202 | if (isDebug) {
203 | if (msg != "OK") {
204 | startTime = System.nanoTime()
205 | print msg
206 | } else {
207 | println "$msg [in ${((double) System.nanoTime() - startTime) / 1000000.0} ms]"
208 | startTime = 0
209 | }
210 | }
211 | }
212 |
213 |
214 |
--------------------------------------------------------------------------------
/gitcatfile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env groovy
2 | import java.util.zip.DataFormatException
3 | import java.util.zip.Inflater
4 |
5 | def cli = new CliBuilder(usage: 'gitcatfile [-e ]')
6 |
7 | cli.with {
8 | h longOpt: 'help', 'Show usage information'
9 | e longOpt: 'extract', args: 1, argName: 'extract', 'Extracts the object in given path'
10 | }
11 |
12 | extract = ""
13 |
14 | def options = cli.parse(args)
15 | if (!options) {
16 | return
17 | }
18 | if (options.h) {
19 | cli.usage()
20 | return
21 | }
22 | if (options.e) {
23 | extract = options.extract
24 | } else {
25 | cli.usage()
26 | return
27 | }
28 |
29 | if (extract) {
30 | def file = new File(extract)
31 | if (! file.exists()) {
32 | println "Object file does not exist in ${file.absolutePath}"
33 | return
34 | }
35 | decompress(file.bytes)
36 | }
37 |
38 | def decompress(byte[] data) throws IOException, DataFormatException {
39 | Inflater inflater = new Inflater()
40 | inflater.setInput(data)
41 |
42 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length)
43 | byte[] buffer = new byte[1024]
44 | while (!inflater.finished()) {
45 | int count = inflater.inflate(buffer)
46 | outputStream.write(buffer, 0, count)
47 | }
48 | outputStream.close()
49 | byte[] output = outputStream.toByteArray()
50 |
51 | def normalizedOutput = []
52 |
53 | def isHex = false
54 | def hexCount = 0
55 | def isHeader = true
56 | output.each {
57 | if (it == 0 && !isHex) {
58 | normalizedOutput << "\\0 "
59 | if (!isHeader) {
60 | isHex = true
61 | } else {
62 | normalizedOutput << "\n"
63 | }
64 | isHeader = false
65 | } else if (isHex & hexCount <= 20 ) {
66 | normalizedOutput << String.format("%02x", it&0xff)
67 |
68 | hexCount++
69 | if (hexCount == 20) {
70 | hexCount = 0
71 | isHex = false
72 | normalizedOutput << "\n"
73 | }
74 | } else {
75 | normalizedOutput << new String(it)
76 | }
77 | }
78 | normalizedOutput.each {
79 | print it
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/live-findfiles:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | path=.
4 | if [ -n "$1" ]; then
5 | path=$1
6 | fi
7 |
8 | while :
9 | do
10 | clear
11 | find $path -type f -print
12 | sleep 1
13 | done
14 |
--------------------------------------------------------------------------------
/live-git-index:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | usage() {
4 | echo "USAGE: live-git-index [-a]"
5 | echo " -a: [optional] show objects of all branches"
6 | exit 1
7 | }
8 |
9 | all=
10 |
11 | while getopts ":a" option; do
12 | case "${option}" in
13 | a)
14 | all=1 ;;
15 | *)
16 | usage ;;
17 | esac
18 | done
19 | shift $((OPTIND-1))
20 |
21 | while :
22 | do
23 | clear
24 | echo "HEAD:"
25 | echo "$( git ls-files -s )"
26 | echo ""
27 |
28 | if [ -n "$all" ]; then
29 | while read branch ; do
30 | echo "${branch}:"
31 | echo "$( git ls-tree -r $branch )"
32 | echo ""
33 | done < <( git for-each-ref --sort=-committerdate refs/heads/ --format='%(refname:short)' )
34 | fi
35 | sleep 1
36 | done
37 |
--------------------------------------------------------------------------------
/live-git-log:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | usage() {
6 | echo "USAGE: live-git-log [-c | --count ]"
7 | exit 1
8 | }
9 |
10 | count=20
11 |
12 | realargs="$@"
13 | while [ $# -gt 0 ]; do
14 | case "$1" in
15 | -c | --count)
16 | count=$2
17 | break
18 | ;;
19 | *)
20 | usage
21 | ;;
22 | esac
23 | shift
24 | done
25 | set -- $realargs
26 |
27 | while :
28 | do
29 | clear
30 | git --no-pager log -${count} --graph --all --pretty=format:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative --date-order
31 | sleep 1
32 | done
33 |
--------------------------------------------------------------------------------
/live-git-reflog:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | usage() {
6 | echo "USAGE: live-git-reflog [-c | --count ]"
7 | exit 1
8 | }
9 |
10 | count=20
11 |
12 | realargs="$@"
13 | while [ $# -gt 0 ]; do
14 | case "$1" in
15 | -c | --count)
16 | count=$2
17 | break
18 | ;;
19 | *)
20 | usage
21 | ;;
22 | esac
23 | shift
24 | done
25 | set -- $realargs
26 |
27 | while :
28 | do
29 | clear
30 | git --no-pager reflog show -${count}
31 | sleep 1
32 | done
33 |
--------------------------------------------------------------------------------
/live-git-status:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | watch -c -exec bash -c "git status -uall -s"
4 |
--------------------------------------------------------------------------------
/live-tree:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | while :
4 | do
5 | clear
6 | tree $1
7 | sleep 1
8 | done
9 |
--------------------------------------------------------------------------------