New upstream version 3.0.0
Rémi Vanicat
2 years ago
0 | Asking for help | |
1 | =============== | |
2 | ||
3 | To ask for help please use the **Discussions** feature. To open | |
4 | a new discussion, click [here][new] and then click on <kbd>Select | |
5 | Category</kbd>, most likely to select the **Q&A** category. | |
6 | ||
7 | Alternatively you can ask for help on the Emacs [StackExchange][se] | |
8 | site (using the `magit` tag) or on the Emacs [subreddit]. | |
9 | ||
0 | 10 | Reporting issues and suggesting features |
1 | 11 | ======================================== |
2 | 12 | |
32 | 42 | [monetary donation][donations]. |
33 | 43 | |
34 | 44 | |
35 | [donations]: https://magit.vc/donate/ | |
36 | [issues]: https://github.com/magit/magit/issues | |
37 | [metadocs]: https://github.com/magit/magit/wiki/Documentation-tools-and-conventions | |
38 | [pulls]: https://github.com/magit/magit/pulls | |
45 | [discussions]: https://github.com/magit/magit/discussions | |
46 | [donations]: https://magit.vc/donate/ | |
47 | [issues]: https://github.com/magit/magit/issues | |
48 | [metadocs]: https://github.com/magit/magit/wiki/Documentation-tools-and-conventions | |
49 | [new]: https://github.com/magit/magit/discussions/new | |
50 | [pulls]: https://github.com/magit/magit/pulls | |
51 | [se]: https://emacs.stackexchange.com | |
52 | [subreddit]: https://www.reddit.com/r/emacs |
0 | 0 | --- |
1 | 1 | title: |
2 | 2 | name: Bug report |
3 | about: Report a defect | |
3 | about: Report a defect. | |
4 | 4 | --- |
5 | 5 | |
6 | 6 | Please do not ignore these instructions. |
0 | 0 | blank_issues_enabled: false |
1 | 1 | contact_links: |
2 | - name: Emacs StackExchange | |
2 | - name: "SUPPORT ☛ The Discussions feature of this repository" | |
3 | url: https://github.com/magit/magit/discussions | |
4 | about: Please search, ask and answer questions here. | |
5 | - name: "SUPPORT ☛ Emacs StackExchange" | |
3 | 6 | url: https://emacs.stackexchange.com/questions/tagged/magit |
4 | about: Please search, ask and answer questions here. | |
5 | - name: Emacs Reddit | |
7 | about: Another place to search, ask and answer questions. | |
8 | - name: "SUPPORT ☛ Emacs Reddit" | |
6 | 9 | url: https://www.reddit.com/r/emacs |
7 | about: Another place to ask questions. | |
8 | - name: Magit FAQ | |
10 | about: Another place to search, ask and answer questions. | |
11 | - name: "Magit FAQ" | |
9 | 12 | url: https://magit.vc/manual/magit/FAQ.html |
10 | 13 | about: It might be that many others had the same question. |
11 | - name: Magit Manual | |
14 | - name: "Magit Manual" | |
12 | 15 | url: https://magit.vc/manual/magit/#Top |
13 | 16 | about: The fine manual may also be of use. |
0 | 0 | --- |
1 | 1 | title: |
2 | 2 | name: Feature request |
3 | about: Suggest a new feature | |
3 | about: Suggest a new feature. ⚠ PLEASE DO NOT USE THIS FOR SUPPORT REQUESTS. ⚠ | |
4 | 4 | --- |
5 | 5 | |
6 | 6 | Before you ask for a new feature to be added to Magit or for an existing feature to be improved, please make sure that what are asking for does not already exist by consulting the documentation [1]. |
0 | name: test | |
1 | on: [ push, pull_request ] | |
2 | jobs: | |
3 | test: | |
4 | runs-on: ubuntu-20.04 | |
5 | strategy: | |
6 | matrix: | |
7 | emacs_version: | |
8 | - 25.1 | |
9 | # 25.2 is identical to 25.3 except for a critical security bug in | |
10 | # enriched text mode (see Emacs Bug#28350). | |
11 | - 25.3 | |
12 | - 26.1 # Debian is on this version. | |
13 | - 26.3 | |
14 | - 27.1 | |
15 | - snapshot | |
16 | git_impl: | |
17 | - git | |
18 | - libgit | |
19 | steps: | |
20 | - uses: cachix/install-nix-action@v12 | |
21 | with: | |
22 | nix_path: nixpkgs=channel:nixos-unstable | |
23 | - uses: cachix/cachix-action@v8 | |
24 | with: | |
25 | name: emacs-ci | |
26 | - uses: actions/checkout@v2 | |
27 | - name: Install | |
28 | run: | | |
29 | # Build and install Emacs (+ magit dependencies) using Nix | |
30 | emacs_ci_version=$(echo "emacs-${{ matrix.emacs_version }}" | sed -e "s/\./-/g") | |
31 | nix-env -f ./t/default.nix -iA $emacs_ci_version | |
32 | emacs --version | |
33 | ||
34 | # Configure Git | |
35 | git config --global user.name "A U Thor" | |
36 | git config --global user.email a.u.thor@example.com | |
37 | git tag 0 | |
38 | - name: Test | |
39 | run: | | |
40 | make test-${{ matrix.git_impl }} DASH_DIR=$PWD |
7 | 7 | Chillar Anand <anand21nanda@gmail.com> |
8 | 8 | Christophe Junke <junke.christophe@gmail.com> <christophe.junke@parrot.com> |
9 | 9 | Damien Cassou <damien@cassou.me> <damien.cassou@gmail.com> |
10 | Daniel Fleischer <danflscr@gmail.com> danielfleischer <daniel.fleischer@amobee.com> | |
10 | 11 | David Abrahams <dave@boostpro.com> |
11 | 12 | Dean Kariniemi <8913263+d3k4r@users.noreply.github.com> |
12 | 13 | Dennis Paskorz <dennis@walltowall.com> |
19 | 20 | Ivan Brennan <ivan.brennan@gmail.com> |
20 | 21 | Jesse Alama <jesse.alama@gmail.com> <alama@stanford.edu> |
21 | 22 | Joakim Jalap <JOJA@stoneridge.com> |
23 | Johann Klähn <johann@jklaehn.de> <kljohann@gmail.com> | |
22 | 24 | Jon Vanderwijk <jonathn@github.com> |
23 | 25 | Jonas Bernoulli <jonas@bernoul.li> |
24 | 26 | Jonas Bernoulli <jonas@bernoul.li> <jonasbernoulli@gmail.com> |
41 | 43 | Natalie Weizenbaum <nex342@gmail.com> Nathan Weizenbaum |
42 | 44 | Noam Postavsky <npostavs@users.sourceforge.net> |
43 | 45 | Noam Postavsky <npostavs@users.sourceforge.net> <github.10.npostavs@spamgourmet.com> |
46 | Pancho Horrillo <pancho@pancho.name> | |
44 | 47 | Peter J. Weisberg <pj@irregularexpressions.net> |
45 | 48 | Peter Vasil <mail@petervasil.net> |
46 | 49 | Phil Sainty <phil@catalyst.net.nz> <phil-s@users.noreply.github.com> |
53 | 56 | Sylvain Rousseau <thisirs@gmail.com> |
54 | 57 | Syohei Yoshida <syohex@gmail.com> |
55 | 58 | Sébastien Gross <seb@chezwam.org> <seb•ɑƬ•chezwam•ɖɵʈ•org> |
59 | Thierry Volpiatto <thievol@posteo.net> <thierry.volpiatto@gmail.com> | |
56 | 60 | Tunc Uzlu <bb2020@users.noreply.github.com> |
57 | 61 | Wei Huang <weih@opera.com> |
58 | 62 | Wilfred Hughes <me@wilfred.me.uk> <whughes@ahl.com> |
0 | language: generic | |
1 | os: linux | |
2 | dist: xenial | |
3 | ||
4 | env: | |
5 | global: | |
6 | - CURL="curl -fsSkL --retry 9 --retry-delay 9" | |
7 | - GHRAW="https://raw.githubusercontent.com" | |
8 | - BUILD_MAGIT_LIBGIT="false" | |
9 | jobs: | |
10 | - EMACS_VERSION=25.1 | |
11 | # 25.2 is identical to 25.3 except for a critical security bug in | |
12 | # enriched text mode (see Emacs Bug#28350). | |
13 | - EMACS_VERSION=25.3 | |
14 | - EMACS_VERSION=26.1 # Debian is on this version. | |
15 | - EMACS_VERSION=26.3 | |
16 | - EMACS_VERSION=27 # 27.0.90, emacs-27 branch, built daily | |
17 | - EMACS_VERSION=master # 28.0.50, master branch, built daily | |
18 | ||
19 | jobs: | |
20 | allow_failures: | |
21 | - env: EMACS_VERSION=master | |
22 | ||
23 | install: | |
24 | - $CURL -O https://github.com/npostavs/emacs-travis/releases/download/bins/emacs-bin-${EMACS_VERSION}.tar.gz | |
25 | - tar -xaf emacs-bin-${EMACS_VERSION}.tar.gz -C / | |
26 | - export EMACS=/tmp/emacs/bin/emacs | |
27 | - $CURL -O ${GHRAW}/magnars/dash.el/master/dash.el | |
28 | - $CURL -O ${GHRAW}/magit/transient/master/lisp/transient.el | |
29 | - $CURL -O ${GHRAW}/magit/with-editor/master/with-editor.el | |
30 | - $EMACS -Q --batch -L . -f batch-byte-compile dash.el transient.el with-editor.el | |
31 | - $EMACS --version | |
32 | ||
33 | script: | |
34 | - git config --global user.name "A U Thor" | |
35 | - git config --global user.email a.u.thor@example.com | |
36 | - git tag 0 | |
37 | - make lisp EMACSBIN=$EMACS DASH_DIR=$PWD | |
38 | - make test EMACSBIN=$EMACS DASH_DIR=$PWD | |
39 | ||
40 | notifications: | |
41 | email: | |
42 | # Default is change, but that includes a new branch's 1st success. | |
43 | on_success: never | |
44 | on_failure: always # The default. |
64 | 64 | - Andrei Chițu <andrei.chitu1@gmail.com> |
65 | 65 | - Andrew Eggenberger <andrew.eggenberger@gmail.com> |
66 | 66 | - Andrew Kirkpatrick <andrew.kirkpatrick@adelaide.edu.au> |
67 | - Andrew Psaltis <apsaltis@vmware.com> | |
67 | 68 | - Andrew Schwartzmeyer <andrew@schwartzmeyer.com> |
68 | 69 | - Andrey Smirnov <andrew.smirnov@gmail.com> |
69 | 70 | - Andriy Kmit' <dev@madand.net> |
71 | 72 | - Aria Edmonds <aria@ar1as.space> |
72 | 73 | - Arialdo Martini <arialdomartini@gmail.com> |
73 | 74 | - Arnau Roig Ninerola <arnau.ninerola@outlook.com> |
75 | - Ashlynn Anderson <pea@pea.sh> | |
74 | 76 | - Barak A. Pearlmutter <barak+git@pearlmutter.net> |
75 | 77 | - Bar Magal <bmagamb@gmail.com> |
76 | 78 | - Bart Bakker <bart@thesoftwarecraft.com> |
82 | 84 | - Bob Uhl <buhl@zvelo.com> |
83 | 85 | - Bradley Wright <brad@intranation.com> |
84 | 86 | - Brandon W Maister <quodlibetor@gmail.com> |
87 | - Brian Leung <leungbk@mailfence.com> | |
85 | 88 | - Brian Warner <warner@lothar.com> |
86 | 89 | - Bryan Shell <bryan.shell@orbitz.com> |
87 | 90 | - Buster Copley <buster@buster.me.uk> |
102 | 105 | - Craig Andera <candera@wangdera.com> |
103 | 106 | - Dale Hagglund <dale.hagglund@gmail.com> |
104 | 107 | - Damien Cassou <damien@cassou.me> |
108 | - Dan Davison <dandavison7@gmail.com> | |
105 | 109 | - Dan Erikson <derikson3@gmail.com> |
106 | 110 | - Daniel Brockman <daniel@gointeractive.se> |
107 | 111 | - Daniel Farina <drfarina@acm.org> |
112 | - Daniel Fleischer <danflscr@gmail.com> | |
108 | 113 | - Daniel Gröber <daniel@dps.uibk.ac.at> |
109 | 114 | - Daniel Hackney <dan@haxney.org> |
110 | 115 | - Daniel Kraus <daniel@kraus.my> |
127 | 132 | - Duianto Vebotci <vebotci@openmailbox.org> |
128 | 133 | - Eli Barzilay <eli@barzilay.org> |
129 | 134 | - Eric Davis <ed@npri.org> |
135 | - Eric <e.a.gebhart@gmail.com> | |
130 | 136 | - Eric Prud'hommeaux <eric@w3.org> |
131 | 137 | - Eric Schulte <schulte.eric@gmail.com> |
132 | 138 | - Erik Anderson <erikbpanderson@gmail.com> |
146 | 152 | - Graham Dobbins <gdobbins@protonmail.com> |
147 | 153 | - Greg A. Woods <woods@planix.com> |
148 | 154 | - Greg Lucas <greg@glucas.net> |
155 | - Gregory Heytings <ghe@sdf.org> | |
149 | 156 | - Greg Sexton <gregsexton@gmail.com> |
150 | 157 | - Guillaume Martres <smarter@ubuntu.com> |
151 | 158 | - Hannu Koivisto <azure@iki.fi> |
152 | 159 | - Hans-Peter Deifel <hpdeifel@gmx.de> |
153 | 160 | - Hussein Ait-Lahcen <hussein.ait-lahcen@fretlink.com> |
154 | 161 | - Ian Eure <ian.eure@gmail.com> |
162 | - Ian Milligan <ianmllgn@gmail.com> | |
163 | - Ilya Grigoriev <ilyagr@users.noreply.github.com> | |
164 | - Ingmar Sittl <ingmar.sittl@elektrobit.com> | |
155 | 165 | - Ingo Lohmar <i.lohmar@gmail.com> |
156 | 166 | - Ioan-Adrian Ratiu <adi@adirat.com> |
157 | 167 | - Ivan Brennan <ivan.brennan@gmail.com> |
164 | 174 | - Jim Blandy <jimb@red-bean.com> |
165 | 175 | - Joakim Jalap <JOJA@stoneridge.com> |
166 | 176 | - Johannes Altmanninger <aclopte@gmail.com> |
167 | - Johann Klähn <kljohann@gmail.com> | |
177 | - Johann Klähn <johann@jklaehn.de> | |
168 | 178 | - John Mastro <john.b.mastro@gmail.com> |
169 | 179 | - John Morris <john@zultron.com> |
170 | 180 | - John Wiegley <johnw@newartisans.com> |
171 | 181 | - Jonas Bernoulli <jonas@bernoul.li> |
182 | - Jonas Galvão Xavier <jonas.agx@gmail.com> | |
172 | 183 | - Jonathan Arnett <jonathan@scriptdrop.co> |
184 | - Jonathan del Strother <me@delstrother.com> | |
173 | 185 | - Jonathan Leech-Pepin <jonathan.leechpepin@gmail.com> |
174 | 186 | - Jonathan Roes <jroes@jroes.net> |
175 | 187 | - Jon Vanderwijk <jonathn@github.com> |
197 | 209 | - Lele Gaifax <lele@metapensiero.it> |
198 | 210 | - Leo Liu <sdl.web@gmail.com> |
199 | 211 | - Leonardo Etcheverry <leo@kalio.net> |
212 | - Leo Vivier <leo.vivier+dev@gmail.com> | |
200 | 213 | - Lingchao Xin <douglarek@users.noreply.github.com> |
201 | 214 | - Li-Yun Chang <michael142536@gmail.com> |
202 | 215 | - Lluís Vilanova <vilanova@ac.upc.edu> |
219 | 232 | - Mark Hepburn <Mark.Hepburn@csiro.au> |
220 | 233 | - Mark Karpov <markkarpov@opmbx.org> |
221 | 234 | - Mark Oteiza <mvoteiza@udel.edu> |
235 | - Martin Joerg <martin.joerg@gmail.com> | |
236 | - Martin Polden <mpolden@yahoo-inc.com> | |
222 | 237 | - Matthew Fluet <matthew.fluet@gmail.com> |
238 | - Matthew Kraai <kraai@ftbfs.org> | |
223 | 239 | - Matthieu Hauglustaine <matt.hauglustaine@gmail.com> |
224 | 240 | - Matus Goljer <dota.keys@gmail.com> |
241 | - Maxim Cournoyer <maxim.cournoyer@gmail.com> | |
225 | 242 | - Michael Fogleman <michaelwfogleman@gmail.com> |
226 | 243 | - Michael Griffiths <mikey@cich.li> |
227 | 244 | - Michael Heerdegen <michael_heerdegen@web.de> |
245 | 262 | - Nikolay Martynov <mar.kolya@gmail.com> |
246 | 263 | - Noam Postavsky <npostavs@users.sourceforge.net> |
247 | 264 | - N. Troy de Freitas <me@ntdef.com> |
265 | - Ola x Nilsson <olani@axis.com> | |
248 | 266 | - Ole Arndt <oliver.arndt@cegedim.com> |
249 | 267 | - Oleh Krehel <ohwoeowho@gmail.com> |
250 | 268 | - Orivej Desh <orivej@gmx.fr> |
251 | 269 | - Óscar Fuentes <ofv@wanadoo.es> |
270 | - Pancho Horrillo <pancho@pancho.name> | |
252 | 271 | - Paul Stadig <paul@stadig.name> |
253 | 272 | - Pavel Holejsovsky <pavel.holejsovsky@upek.com> |
254 | 273 | - Pekka Pessi <nospam@pessi.fi> |
257 | 276 | - Peter J. Weisberg <pj@irregularexpressions.net> |
258 | 277 | - Peter Vasil <mail@petervasil.net> |
259 | 278 | - Philippe Vaucher <philippe.vaucher@gmail.com> |
279 | - Philipp Fehre <pfehre@twitter.com> | |
260 | 280 | - Philipp Haselwarter <philipp@haselwarter.org> |
261 | 281 | - Philipp Stephani <phst@google.com> |
262 | 282 | - Philip Weaver <philip.weaver@gmail.com> |
265 | 285 | - Pierre Neidhardt <ambrevar@gmail.com> |
266 | 286 | - Pieter Praet <pieter@praet.org> |
267 | 287 | - Prathamesh Sonpatki <csonpatki@gmail.com> |
288 | - Pritam Baral <pritam@pritambaral.com> | |
268 | 289 | - rabio <rabiodev@o2.pl> |
269 | 290 | - Radon Rosborough <radon.neon@gmail.com> |
270 | 291 | - Rafael Laboissiere <rafael@laboissiere.net> |
284 | 305 | - Rüdiger Sonderfeld <ruediger@c-plusplus.net> |
285 | 306 | - Russell Black <black.russell@gmail.com> |
286 | 307 | - Ryan C. Thompson <rct@thompsonclan.org> |
308 | - Sam Cedarbaum <scedarbaum@gmail.com> | |
287 | 309 | - Samuel Bronson <naesten@gmail.com> |
288 | 310 | - Samuel W. Flint <swflint@flintfam.org> |
289 | 311 | - Sanjoy Das <sanjoy@playingwithpointers.com> |
297 | 319 | - Sergey Vinokurov <serg.foo@gmail.com> |
298 | 320 | - Servilio Afre Puentes <afrepues@mcmaster.ca> |
299 | 321 | - Silent Sphere <silentsphere110@gmail.com> |
322 | - Simon Pintarelli <simon.pintarelli@cscs.ch> | |
300 | 323 | - Štěpán Němec <stepnem@gmail.com> |
301 | 324 | - Steven Chow <steve@myfreestuffapp.com> |
302 | 325 | - Steven E. Harris <seh@panix.com> |
306 | 329 | - Suhail Shergill <suhailshergill@gmail.com> |
307 | 330 | - Sylvain Rousseau <thisirs@gmail.com> |
308 | 331 | - Syohei Yoshida <syohex@gmail.com> |
332 | - Szunti <Szunti@users.noreply.github.com> | |
309 | 333 | - Takafumi Arakaki <aka.tkf@gmail.com> |
310 | 334 | - Tassilo Horn <tsdh@gnu.org> |
311 | 335 | - Teemu Likonen <tlikonen@iki.fi> |
312 | 336 | - Teruki Shigitani <teruki.shigitani@gmail.com> |
313 | - Thierry Volpiatto <thierry.volpiatto@gmail.com> | |
337 | - Thierry Volpiatto <thievol@posteo.net> | |
314 | 338 | - Thomas A Caswell <tcaswell@gmail.com> |
315 | 339 | - Thomas Fini Hansen <xen@xen.dk> |
316 | 340 | - Thomas Frössman <thomasf@jossystem.se> |
329 | 353 | - Vineet Naik <vineet@helpshift.com> |
330 | 354 | - Vitaly Ostashov <hotosho@yandex-team.ru> |
331 | 355 | - Vladimir Panteleev <git@thecybershadow.net> |
356 | - Vladimir Sedach <vas@oneofus.la> | |
332 | 357 | - Wei Huang <weih@opera.com> |
333 | 358 | - Wilfred Hughes <me@wilfred.me.uk> |
334 | 359 | - Win Treese <treese@acm.org> |
360 | - Wojciech Siewierski <wojciech@siewierski.eu> | |
335 | 361 | - Wouter Bolsterlee <wouter@bolsterl.ee> |
336 | 362 | - Xavier Noria <fxn@hashref.com> |
337 | 363 | - Xu Chunyang <mail@xuchunyang.me> |
364 | - Yann Herklotz <git@yannherklotz.com> | |
338 | 365 | - Yann Hodique <yann.hodique@gmail.com> |
339 | 366 | - Ynilu <ynilu.chang@gmail.com> |
340 | 367 | - York Zhao <gtdplatform@gmail.com> |
22 | 22 | @echo $^ | xargs -n 1 $(INSTALL_INFO) --dir=$@ |
23 | 23 | |
24 | 24 | HTML_FIXUP_CSS = '/<link rel="stylesheet" type="text\/css" href="\/assets\/page.css">/a\ |
25 | <link class="s-css-s--style" rel="stylesheet" title="Default" href="/assets/themes/default.css">\ | |
25 | <link rel="icon" href="/assets/magit_alt1.ico">\ | |
26 | \n<link class="s-css-s--style" rel="stylesheet" title="Default" href="/assets/themes/default.css">\ | |
26 | 27 | \n<link class="s-css-s--style" rel="stylesheet alternate" title="Default high contrast" href="/assets/themes/default-high-contrast.css">\ |
27 | 28 | \n<link class="s-css-s--style" rel="stylesheet alternate" title="Solarized dark xterm" href="/assets/themes/solarized-dark-xterm.css">\ |
28 | 29 | \n<link class="s-css-s--style" rel="stylesheet alternate" title="Black on white" href="/assets/themes/black-on-white.css">\ |
106 | 107 | |
107 | 108 | stats: |
108 | 109 | @printf "Generating statistics\n" |
109 | @gitstats -c style=/assets/stats.css -c max_authors=999 $(TOP) $(statsdir) | |
110 | @gitstats -c style=https://magit.vc/assets/stats.css -c max_authors=999 $(TOP) $(statsdir) | |
110 | 111 | |
111 | 112 | authors: AUTHORS.md |
112 | 113 |
0 | * Magit v3.0.0 Release Notes (unreleased) | |
0 | * It's Magit! A Git Porcelain inside Emacs | |
1 | ||
2 | Magit is a text-based Git user interface that puts an unmatched focus | |
3 | on streamlining workflows. Commands are invoked using short mnemonic | |
4 | key sequences that take the cursor’s position in the highly actionable | |
5 | interface into account to provide context-sensitive behavior. | |
6 | ||
7 | With Magit you can do nearly everything that you can do when using Git | |
8 | on the command-line, but at greater speed and while taking advantage | |
9 | of advanced features that previously seemed too daunting to use on a | |
10 | daily basis. Many users will find that by using Magit they can become | |
11 | more effective Git user. | |
12 | ||
13 | For more information about Magit, see https://magit.vc. | |
14 | ||
15 | * Magit v3.0.0 Release Notes | |
16 | ||
17 | Released 25th May 2021 by Jonas Bernoulli. | |
18 | ||
19 | I am pleased to announce the release of Magit version 3.0.0, | |
20 | representing 1264 commits by 87 contributors over 2.5 years. | |
21 | ||
22 | Also see https://emacsair.me/2021/05/25/magit-3.0. | |
23 | ||
1 | 24 | ** Breaking changes |
2 | 25 | |
3 | 26 | - Dropped support for Git v2.0 and v2.1. |
16 | 39 | |
17 | 40 | - The commands ~magit-branch-pull-request~, ~magit-checkout-pull-request~ |
18 | 41 | and ~magit-worktree-checkout-pull-request~ were removed in favor of |
19 | improved implementations provided by the new Forge package. | |
42 | improved implementations provided by the new Forge package. (See | |
43 | https://emacsair.me/2018/12/19/forge-0.1 for more information about | |
44 | Forge.) | |
20 | 45 | |
21 | 46 | - ~C-c C-e~ is no longer bound to ~magit-dispatch-popup~. It is bound to |
22 | 47 | ~magit-edit-thing~ now, so that Forge can add section-specific |
38 | 63 | compatibility. Packages using the obsolete variable and functions |
39 | 64 | should be adjusted soon. #3836 |
40 | 65 | |
66 | - Magit-Section is now distributed as a separate package, as announced | |
67 | here: https://emacsair.me/2020/01/23/magit-section. #4003 | |
68 | ||
69 | - Magit now adds three global key bindings, which can be prevented | |
70 | by setting the new option ~magit-define-global-key-bindings~ before | |
71 | loading ~magit~. Note that if you bind these keys to other commands | |
72 | anywhere in your init file (even *after* loading ~magit~), then Magit | |
73 | won't override those bindings. See the options doc-string for | |
74 | more information. #4237 | |
75 | ||
76 | - Magit no longer depends on ~async-bytecomp~ to avoid a certain class | |
77 | of mystery bugs because this effort backfired. 86eec7ba3 | |
78 | ||
79 | - ~global-git-commit-mode~ is no longer autoloaded. Users who commit | |
80 | from the command-line but still want to use ~git-commit-mode~, might | |
81 | now have to load ~git-commit~ explicitly in their init file. | |
82 | 13f20763a | |
83 | ||
41 | 84 | ** Changes since v2.90.0 |
42 | 85 | |
43 | 86 | - It isn't always obvious that a section can be expanded, especially |
84 | 127 | to drop such commits, then you have to enable ~--autosquash~ in the |
85 | 128 | popup and then invoke ~magit-rebase-interactive~. #3670 |
86 | 129 | |
130 | - ~magit-rebase-remove-commit~ now supports removing the ~HEAD~ | |
131 | commit. #4195 | |
132 | ||
87 | 133 | - The option ~magit-repository-directories~ defaults to ~nil~ again |
88 | 134 | because the non-nil default added in v2.90.0 led to surprising |
89 | 135 | changes in behavior. The documentation of this option and the |
137 | 183 | - New command ~git-rebase-break~ inserts a "break" action in the |
138 | 184 | rebase to-do sequence (available as of Git v2.20). #3762 |
139 | 185 | |
186 | - ~git-rebase-kill-line~ and the commands for changing the action of a | |
187 | commit line (e.g., ~git-rebase-squash~) learned to work on all lines | |
188 | selected by the region. #4172 | |
189 | ||
140 | 190 | - The ~--color-moved~ diff argument is supported now, but isn't |
141 | 191 | available from the diff transients by default. To enable it |
142 | 192 | use "C-x l" in those transients. #3424 |
149 | 199 | - ~magit-cherry~ is now available from the ~magit-dispatch~ prefix. |
150 | 200 | ef311f378 |
151 | 201 | |
152 | ~ ~magit-cherry-spinoff~ now offers the upstream as the default | |
202 | - ~magit-cherry-spinoff~ now offers the upstream as the default | |
153 | 203 | starting-point. e5a2a0ac2 |
154 | 204 | |
155 | 205 | - Added new command ~magit-branch-spinout~. #3794. |
174 | 224 | |
175 | 225 | - Added new option ~magit-worktree-read-directory-name-function~. #3820 |
176 | 226 | |
177 | - TODO Added basic support for libgit2. #3841 | |
227 | - Basic optional support for ~libgit2~ was added, but because so few | |
228 | functions are currently implemented using that library, opting in | |
229 | currently has almost no effect. #3841 | |
178 | 230 | |
179 | 231 | - ~git bisect~ is now run asynchronously. #3802 |
232 | ||
233 | - ~magit-bisect~ now supports specifying alternate terms. The new | |
234 | infixes and suffix related to this functionality are disabled by | |
235 | default. | |
180 | 236 | |
181 | 237 | - ~magit-branch-or-commit-at-point~ now falls back to an abbreviated |
182 | 238 | hash instead of something like "master~2", because the latter often |
330 | 386 | available from the diff transients by default. To enable it use |
331 | 387 | "C-x l" in those transients. #4056 |
332 | 388 | |
389 | - Added new command ~magit-reset-keep~. 0ea8b0ef6 | |
390 | ||
391 | - Added new option ~magit-reshelve-since-committer-only~. #4101 | |
392 | ||
393 | - Added new command ~magit-commit-absorb~ as an alternative to | |
394 | ~magit-commit-autofixup~. 9423edc0b | |
395 | ||
396 | - Added new option ~magit-status-use-buffer-arguments~. #4046 | |
397 | ||
398 | - Added new command ~magit-project-status~. #4173 | |
399 | ||
400 | - Added new variable ~magit-process-extreme-logging~ for debugging | |
401 | purposes. #4217 | |
402 | ||
403 | - Taught Isearch and Swiper how to expand Magit sections when the | |
404 | current match is inside a hidden section and how to close sections | |
405 | again. #3999 | |
406 | ||
407 | - Added new command ~magit-commit-absorb-modules~. 10b4bec53 | |
408 | ||
409 | - Added new transient command ~magit-shortlog~. #4262 | |
410 | ||
411 | - Added new command ~magit-generate-changelog~. c5e118111 | |
412 | ||
413 | - The name of the main branch is no longer hard-coded to "master". | |
414 | Now we use the value of ~init.defaultBranch~ if that is set and the | |
415 | named branch exists. If not, then some other names that are | |
416 | commonly used for the main branch are tried as a potential fallback. | |
417 | c4494ac0b | |
418 | ||
419 | - Added new option ~magit-diff-extra-stat-arguments~. 1bd4fe26e | |
420 | ||
421 | - Added support for ~git-credential-manager-core~. #4318 | |
422 | ||
423 | - The name of the upstream remote is no longer hard-code to "origin". | |
424 | See the doc-string of function ~magit-primary-remote~ to learn how to | |
425 | customize this. f883b62fe | |
426 | ||
427 | This release also contains numerous other improvements. | |
428 | ||
333 | 429 | ** Fixes since v2.90.0 |
334 | 430 | |
335 | 431 | - Bumped the minimal required version of ~git-commit~ to the correct |
375 | 471 | - A regression in ~magit-log-move-to-parent~ prevented it from doing its |
376 | 472 | job. #3682 |
377 | 473 | |
474 | - Since v2.11.0 ~magit-log-revision-headers-format~ lines in the log | |
475 | output (shown via ~++header~) weren't displayed properly when | |
476 | ~--graph~ was enabled. #4129 | |
477 | ||
378 | 478 | - ~magit-clone~ didn't run ~magit-credential-hook~. #3683 |
379 | 479 | |
380 | 480 | - ~magit-list-repositories~ failed if one of the repositories that it |
460 | 560 | - Modifying a file, marking it with a "skip-worktree" or "assume |
461 | 561 | unchanged" bit, and then modifying it again triggered a failure in |
462 | 562 | ~magit-wip-commit-worktree~. #4037 |
563 | ||
564 | - ~magit-abbrev-length~ returned an incorrect result when | |
565 | ~core.abbrev~ was explicitly set to "auto". | |
566 | ||
567 | - Calling ~magit-status~ in a repository with a corrupt Git | |
568 | configuration didn't propagate the error and instead preseted the | |
569 | directory as though it was uninitialized. #4337 | |
570 | ||
571 | - When the status buffer is not shown in any buffer but point is on | |
572 | a hunk, and editing and saving the respective file causes, that | |
573 | hunk to disappear or change, then Magit ended up changing point | |
574 | in the file-visiting buffer. #4196 | |
463 | 575 | |
464 | 576 | - Various bug fixes to |
465 | 577 | ~magit-branch-delete~ (3e73ff19d), |
492 | 604 | ~magit-stash-drop~ (a4972766a), |
493 | 605 | ~magit-ignore-submodules-p~ (a7699f868), |
494 | 606 | ~magit-log-propertize-keywords~ (ac1ee3df5), |
495 | ||
496 | This release also contains other minor improvements, bug fixes, typo | |
497 | fixes, and documentation fixes. | |
607 | and then I stopped adding to this list. | |
608 | ||
609 | This release also contains numerous other bug fixes, typo fixes, and | |
610 | documentation fixes. | |
611 | ||
612 | * Authors | |
613 | ||
614 | 1001 Jonas Bernoulli | |
615 | 120 Kyle Meyer | |
616 | 10 Basil L. Contovounesios | |
617 | 9 Noam Postavsky | |
618 | 5 Vladimir Panteleev | |
619 | 4 Damien Cassou | |
620 | 4 Daniel Martín | |
621 | 4 Sam Cedarbaum | |
622 | 4 Štěpán Němec | |
623 | 3 Adam Porter | |
624 | 3 Benjamin Motz | |
625 | 3 Kévin Le Gouguec | |
626 | 2 Alban Gruin | |
627 | 2 Allen Li | |
628 | 2 Bastian Beischer | |
629 | 2 Clément Pit-Claudel | |
630 | 2 Daniel Fleischer | |
631 | 2 Evan Torrie | |
632 | 2 Ingmar Sittl | |
633 | 2 Leo Vivier | |
634 | 2 Martin Polden | |
635 | 2 Naoya Yamashita | |
636 | 2 Phil Sainty | |
637 | 2 Philipp Stephani | |
638 | 2 Radon Rosborough | |
639 | 2 Ryan C. Thompson | |
640 | 2 Szunti | |
641 | 2 Tassilo Horn | |
642 | 2 Thierry Volpiatto | |
643 | 2 Troy Hinckley | |
644 | 2 zilongshanren | |
645 | 1 Adam Kruszewski | |
646 | 1 Adam Spiers | |
647 | 1 Alexander Miller | |
648 | 1 Andrew Eggenberger | |
649 | 1 Andrew Psaltis | |
650 | 1 Andrew Schwartzmeyer | |
651 | 1 Arnau Roig Ninerola | |
652 | 1 Ashlynn Anderson | |
653 | 1 Ben North | |
654 | 1 Brian Leung | |
655 | 1 Dan Davison | |
656 | 1 Danny Zhu | |
657 | 1 David Ellison | |
658 | 1 Dominique Quatravaux | |
659 | 1 Eric | |
660 | 1 Fritz Grabo | |
661 | 1 Gregory Heytings | |
662 | 1 Hussein Ait-Lahcen | |
663 | 1 Ian Milligan | |
664 | 1 Ilya Grigoriev | |
665 | 1 Johann Klähn | |
666 | 1 Johannes Altmanninger | |
667 | 1 Jonas Galvão Xavier | |
668 | 1 Jonathan Arnett | |
669 | 1 Jonathan del Strother | |
670 | 1 Jordan Galby | |
671 | 1 Josh Elsasser | |
672 | 1 Justin Guenther | |
673 | 1 Keshav Kini | |
674 | 1 Kevin Brubeck Unhammer | |
675 | 1 Kevin J. Foley | |
676 | 1 Knut Olav Bøhmer | |
677 | 1 Magnus Malm | |
678 | 1 Mario Rodas | |
679 | 1 Martin Joerg | |
680 | 1 Matthew Kraai | |
681 | 1 Maxim Cournoyer | |
682 | 1 Michael Griffiths | |
683 | 1 Ola x Nilsson | |
684 | 1 Pancho Horrillo | |
685 | 1 Philipp Fehre | |
686 | 1 Pritam Baral | |
687 | 1 Roey Darwish Dror | |
688 | 1 Sean Whitton | |
689 | 1 Simon Pintarelli | |
690 | 1 Steve Purcell | |
691 | 1 Thomas Fini Hansen | |
692 | 1 Topi Miettinen | |
693 | 1 Tsuyoshi Kitamoto | |
694 | 1 Vitaly Ostashov | |
695 | 1 Vladimir Sedach | |
696 | 1 Wojciech Siewierski | |
697 | 1 Yann Herklotz | |
698 | 1 Ynilu | |
699 | 1 Zhu Zihao | |
700 | 1 zakora |
1 | 1 | :PREAMBLE: |
2 | 2 | #+AUTHOR: Jonas Bernoulli |
3 | 3 | #+EMAIL: jonas@bernoul.li |
4 | #+DATE: 2015-2020 | |
4 | #+DATE: 2015-2021 | |
5 | 5 | #+LANGUAGE: en |
6 | 6 | |
7 | 7 | #+TEXINFO_DIR_CATEGORY: Emacs |
8 | 8 | #+TEXINFO_DIR_TITLE: Magit-Section: (magit-section). |
9 | 9 | #+TEXINFO_DIR_DESC: Use Magit sections in your own packages. |
10 | #+SUBTITLE: for version 2.90.1 (v2.90.1-948-ge293416ce+1) | |
10 | #+SUBTITLE: for version 3.0.0 | |
11 | 11 | |
12 | 12 | #+TEXINFO_DEFFN: t |
13 | 13 | #+OPTIONS: H:4 num:3 toc:2 |
25 | 25 | can use sections in your own packages. |
26 | 26 | |
27 | 27 | #+TEXINFO: @noindent |
28 | This manual is for Magit-Section version 2.90.1 (v2.90.1-948-ge293416ce+1). | |
28 | This manual is for Magit-Section version 3.0.0. | |
29 | 29 | |
30 | 30 | #+BEGIN_QUOTE |
31 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@bernoul.li> | |
31 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@bernoul.li> | |
32 | 32 | |
33 | 33 | You can redistribute this document and/or modify it under the terms |
34 | 34 | of the GNU General Public License as published by the Free Software |
61 | 61 | |
62 | 62 | - Function: magit-insert-section [name] (type &optional value hide) &rest body |
63 | 63 | |
64 | Insert a section at point. | |
65 | ||
66 | TYPE is the section type, a symbol. Many commands that act on | |
67 | the current section behave differently depending on that type. | |
68 | Like functions and variables, TYPE must be prefixed with the | |
69 | package name. (For historic reasons the types used by Magit | |
70 | and Forge do not use a package prefix.) | |
71 | ||
72 | Optional VALUE is the value of the section, usually a string | |
73 | that is required when acting on the section. | |
64 | Create a section object of type CLASS, storing VALUE in its | |
65 | ~value~ slot, and insert the section at point. CLASS is a | |
66 | subclass of `magit-section' or has the form ~(eval FORM)~, in | |
67 | which case FORM is evaluated at runtime and should return a | |
68 | subclass. In other places a sections class is oftern referred | |
69 | to as its "type". | |
70 | ||
71 | Many commands behave differently depending on the class of the | |
72 | current section and sections of a certain class can have their | |
73 | own keymap, which is specified using the `keymap' class slot. | |
74 | The value of that slot should be a variable whose value is a | |
75 | keymap. | |
76 | ||
77 | For historic reasons Magit and Forge in most cases use symbols | |
78 | as CLASS that don't actually identify a class and that lack the | |
79 | appropriate package prefix. This works due to some undocumented | |
80 | kludges, which are not available to other packages. | |
74 | 81 | |
75 | 82 | When optional HIDE is non-nil collapse the section body by |
76 | 83 | default, i.e. when first creating the section, but not when |
95 | 102 | of the partially inserted section. This can happen when creating |
96 | 103 | a section by washing Git's output and Git didn't actually output |
97 | 104 | anything this time around. |
98 | ||
99 | For historic reasons, if a variable ~magit-TYPE-section-map~ | |
100 | or ~forge-TYPE-section-map~ exists, then use that as the | |
101 | text-property keymap~ of all text belonging to the section (but | |
102 | this may be overwritten in subsections). TYPE can also have the | |
103 | form ~(eval FORM)~ in which case FORM is evaluated at runtime. | |
104 | 105 | |
105 | 106 | - Function: magit-insert-heading &rest args |
106 | 107 | |
253 | 254 | :END: |
254 | 255 | |
255 | 256 | #+BEGIN_QUOTE |
256 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@bernoul.li> | |
257 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@bernoul.li> | |
257 | 258 | |
258 | 259 | You can redistribute this document and/or modify it under the terms |
259 | 260 | of the GNU General Public License as published by the Free Software |
7 | 7 | |
8 | 8 | @copying |
9 | 9 | @quotation |
10 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@@bernoul.li> | |
10 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@@bernoul.li> | |
11 | 11 | |
12 | 12 | You can redistribute this document and/or modify it under the terms |
13 | 13 | of the GNU General Public License as published by the Free Software |
30 | 30 | @finalout |
31 | 31 | @titlepage |
32 | 32 | @title Magit-Section Developer Manual |
33 | @subtitle for version 2.90.1 (v2.90.1-948-ge293416ce+1) | |
33 | @subtitle for version 3.0.0 | |
34 | 34 | @author Jonas Bernoulli |
35 | 35 | @page |
36 | 36 | @vskip 0pt plus 1filll |
53 | 53 | can use sections in your own packages. |
54 | 54 | |
55 | 55 | @noindent |
56 | This manual is for Magit-Section version 2.90.1 (v2.90.1-948-ge293416ce+1). | |
56 | This manual is for Magit-Section version 3.0.0. | |
57 | 57 | |
58 | 58 | @quotation |
59 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@@bernoul.li> | |
59 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@@bernoul.li> | |
60 | 60 | |
61 | 61 | You can redistribute this document and/or modify it under the terms |
62 | 62 | of the GNU General Public License as published by the Free Software |
99 | 99 | |
100 | 100 | @defun magit-insert-section [name] (type &optional value hide) &rest body |
101 | 101 | |
102 | Insert a section at point. | |
103 | ||
104 | TYPE is the section type, a symbol. Many commands that act on | |
105 | the current section behave differently depending on that type. | |
106 | Like functions and variables, TYPE must be prefixed with the | |
107 | package name. (For historic reasons the types used by Magit | |
108 | and Forge do not use a package prefix.) | |
109 | ||
110 | Optional VALUE is the value of the section, usually a string | |
111 | that is required when acting on the section. | |
102 | Create a section object of type CLASS, storing VALUE in its | |
103 | @code{value} slot, and insert the section at point. CLASS is a | |
104 | subclass of `magit-section' or has the form @code{(eval FORM)}, in | |
105 | which case FORM is evaluated at runtime and should return a | |
106 | subclass. In other places a sections class is oftern referred | |
107 | to as its "type". | |
108 | ||
109 | Many commands behave differently depending on the class of the | |
110 | current section and sections of a certain class can have their | |
111 | own keymap, which is specified using the `keymap' class slot. | |
112 | The value of that slot should be a variable whose value is a | |
113 | keymap. | |
114 | ||
115 | For historic reasons Magit and Forge in most cases use symbols | |
116 | as CLASS that don't actually identify a class and that lack the | |
117 | appropriate package prefix. This works due to some undocumented | |
118 | kludges, which are not available to other packages. | |
112 | 119 | |
113 | 120 | When optional HIDE is non-nil collapse the section body by |
114 | 121 | default, i.e. when first creating the section, but not when |
133 | 140 | of the partially inserted section. This can happen when creating |
134 | 141 | a section by washing Git's output and Git didn't actually output |
135 | 142 | anything this time around. |
136 | ||
137 | For historic reasons, if a variable @code{magit-TYPE-section-map} | |
138 | or @code{forge-TYPE-section-map} exists, then use that as the | |
139 | text-property keymap~ of all text belonging to the section (but | |
140 | this may be overwritten in subsections). TYPE can also have the | |
141 | form @code{(eval FORM)} in which case FORM is evaluated at runtime. | |
142 | 143 | @end defun |
143 | 144 | |
144 | 145 | @defun magit-insert-heading &rest args |
1 | 1 | :PREAMBLE: |
2 | 2 | #+AUTHOR: Jonas Bernoulli |
3 | 3 | #+EMAIL: jonas@bernoul.li |
4 | #+DATE: 2015-2020 | |
4 | #+DATE: 2015-2021 | |
5 | 5 | #+LANGUAGE: en |
6 | 6 | |
7 | 7 | #+TEXINFO_DIR_CATEGORY: Emacs |
8 | 8 | #+TEXINFO_DIR_TITLE: Magit: (magit). |
9 | 9 | #+TEXINFO_DIR_DESC: Using Git from Emacs with Magit. |
10 | #+SUBTITLE: for version 2.90.1 (v2.90.1-954-g509e97b7+1) | |
10 | #+SUBTITLE: for version 3.0.0 | |
11 | 11 | |
12 | 12 | #+TEXINFO_DEFFN: t |
13 | 13 | #+OPTIONS: H:4 num:3 toc:2 |
24 | 24 | Magit and Git itself deserve to be called porcelains. |
25 | 25 | |
26 | 26 | #+TEXINFO: @noindent |
27 | This manual is for Magit version 2.90.1 (v2.90.1-954-g509e97b7+1). | |
27 | This manual is for Magit version 3.0.0. | |
28 | 28 | |
29 | 29 | #+BEGIN_QUOTE |
30 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@bernoul.li> | |
30 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@bernoul.li> | |
31 | 31 | |
32 | 32 | You can redistribute this document and/or modify it under the terms |
33 | 33 | of the GNU General Public License as published by the Free Software |
190 | 190 | with the following content before running ~make~: |
191 | 191 | |
192 | 192 | #+BEGIN_SRC makefile |
193 | LOAD_PATH = -L /path/to/magit/lisp | |
194 | LOAD_PATH += -L /path/to/dash | |
195 | LOAD_PATH += -L /path/to/transient | |
196 | LOAD_PATH += -L /path/to/with-editor | |
193 | LOAD_PATH = -L ~/.emacs.d/site-lisp/magit/lisp | |
194 | LOAD_PATH += -L ~/.emacs.d/site-lisp/dash | |
195 | LOAD_PATH += -L ~/.emacs.d/site-lisp/transient/lisp | |
196 | LOAD_PATH += -L ~/.emacs.d/site-lisp/with-editor | |
197 | 197 | #+END_SRC |
198 | 198 | |
199 | 199 | Finally add this to your init file: |
208 | 208 | "~/.emacs.d/site-lisp/magit/Documentation/")) |
209 | 209 | #+END_SRC |
210 | 210 | |
211 | Of course if you installed the dependencies manually as well, then | |
212 | you have to tell Emacs about them too, by prefixing the above with: | |
213 | ||
214 | #+BEGIN_SRC emacs-lisp | |
215 | (add-to-list 'load-path "~/.emacs.d/site-lisp/dash") | |
216 | (add-to-list 'load-path "~/.emacs.d/site-lisp/transient/lisp") | |
217 | (add-to-list 'load-path "~/.emacs.d/site-lisp/with-editor") | |
218 | #+END_SRC | |
219 | ||
211 | 220 | Note that you have to add the ~lisp~ subdirectory to the ~load-path~, not |
212 | 221 | the top-level of the repository, and that elements of ~load-path~ should |
213 | 222 | not end with a slash, while those of ~Info-directory-list~ should. |
281 | 290 | if you do that, then you should commit all uncommitted changes before |
282 | 291 | proceeding. |
283 | 292 | |
284 | To display information about the current Git repository, type ~M-x | |
285 | magit-status RET~. You will be using this command a lot, and should | |
286 | therefore give it a global key binding. This is what we recommend: | |
287 | ||
288 | #+BEGIN_SRC emacs-lisp | |
289 | (global-set-key (kbd "C-x g") 'magit-status) | |
290 | #+END_SRC | |
293 | Type ~C-x g~ to display information about the current Git repository in | |
294 | a dedicated buffer, called the status buffer. | |
291 | 295 | |
292 | 296 | Most Magit commands are commonly invoked from the status buffer. It |
293 | 297 | can be considered the primary interface for interacting with Git using |
339 | 343 | |
340 | 344 | Now two new buffers appear. One is for writing the commit message, |
341 | 345 | the other shows a diff with the changes that you are about to |
342 | committed. Write a message and then type ~C-c C-c~ to actually create | |
346 | commit. Write a message and then type ~C-c C-c~ to actually create | |
343 | 347 | the commit. |
344 | 348 | |
345 | 349 | You probably don't want to push the commit you just created because |
350 | 354 | push-remote is not configured yet, then you would first be prompted |
351 | 355 | for the remote to push to.) |
352 | 356 | |
353 | So far we have mentioned the commit, push, and log transient prefix | |
354 | commands. These are probably among the transients you will be using | |
355 | the most, but many others exist. To show a transient that lists all | |
356 | other transients (as well as the various apply commands and some other | |
357 | essential commands), type ~h~. Try a few. | |
358 | ||
359 | The key bindings in that transient correspond to the bindings in Magit | |
357 | So far we have mentioned the commit, push, and log menu commands. | |
358 | These are probably among the menus you will be using the most, but | |
359 | many others exist. To show a menu that lists all other menus (as well | |
360 | as the various apply commands and some other essential commands), type | |
361 | ~h~. Try a few. (Such menus are also called "transient prefix | |
362 | commands" or just "transients".) | |
363 | ||
364 | The key bindings in that menu correspond to the bindings in Magit | |
360 | 365 | buffers, including but not limited to the status buffer. So you could |
361 | type ~h d~ to bring up the diff transient, but once you remember that | |
362 | "d" stands for "diff", you would usually do so by just typing ~d~. But | |
363 | this "prefix of prefixes" is useful even once you have memorized all | |
364 | the bindings, as it can provide easy access to Magit commands from | |
365 | non-Magit buffers. You should create a global key binding for this | |
366 | command too: | |
367 | ||
368 | #+BEGIN_SRC emacs-lisp | |
369 | (global-set-key (kbd "C-x M-g") 'magit-dispatch) | |
370 | #+END_SRC | |
371 | ||
372 | In the same vein, you might also want to enable ~global-magit-file-mode~ | |
373 | to get some more Magit key bindings in regular file-visiting buffers | |
374 | (see [[*Minor Mode for Buffers Visiting Files]]). | |
366 | type ~h d~ to bring up the diff menu, but once you remember that "d" | |
367 | stands for "diff", you would usually do so by just typing ~d~. But this | |
368 | "prefix of prefixes" is useful even once you have memorized all the | |
369 | bindings, as it can provide easy access to Magit commands from | |
370 | non-Magit buffers. The global binding is ~C-x M-g~. | |
371 | ||
372 | In file visiting buffers ~C-c M-g~ brings up a similar menu featuring | |
373 | commands that act on just the visited file, see [[*Commands for Buffers | |
374 | Visiting Files]]. | |
375 | 375 | |
376 | 376 | It is not necessary that you do so now, but if you stick with Magit, |
377 | 377 | then it is highly recommended that you read the next section too. |
644 | 644 | by adding a hook, like so: |
645 | 645 | |
646 | 646 | #+BEGIN_SRC emacs-lisp |
647 | (add-hook 'after-save-hook 'magit-after-save-refresh-status t) | |
647 | (with-eval-after-load 'magit-mode | |
648 | (add-hook 'after-save-hook 'magit-after-save-refresh-status t)) | |
648 | 649 | #+END_SRC |
649 | 650 | |
650 | 651 | Automatically refreshing Magit buffers ensures that the displayed |
1483 | 1484 | |
1484 | 1485 | - ~delete-pr-remote~ When deleting a branch that was created from a |
1485 | 1486 | pull-request and if no other branches still exist on that |
1486 | remote, then `magit-branch-delete' offers to delete the remote | |
1487 | remote, then ~magit-branch-delete~ offers to delete the remote | |
1487 | 1488 | as well. This should be safe because it only happens if no |
1488 | 1489 | other refs exist in the remotes namespace, and you can recreate |
1489 | 1490 | the remote if necessary. |
1495 | 1496 | to confirm by accepting the default (or selecting another). |
1496 | 1497 | This action only concerns the deletion of multiple stashes at |
1497 | 1498 | once. |
1499 | ||
1500 | - Publishing: | |
1501 | ||
1502 | - ~set-and-push~ When pushing to the upstream or the push-remote | |
1503 | and that isn't actually configured yet, then the user can first | |
1504 | set the target. If s/he confirms the default too quickly, then | |
1505 | s/he might end up pushing to the wrong branch and if the remote | |
1506 | repository is configured to disallow fixing such mistakes, then | |
1507 | that can be quite embarrassing and annoying. | |
1498 | 1508 | |
1499 | 1509 | - Edit published history: |
1500 | 1510 | |
2533 | 2543 | |
2534 | 2544 | Show log for all references and ~HEAD~. |
2535 | 2545 | |
2536 | ||
2537 | Two additional commands that show the log for the file or blob that | |
2538 | is being visited in the current buffer exists, see [[*Minor Mode for | |
2539 | Buffers Visiting Files]]. The command ~magit-cherry~ also shows a log, | |
2540 | see [[*Cherries]]. | |
2546 | Two additional commands that show the log for the file or blob that is | |
2547 | being visited in the current buffer exists, see [[*Commands for Buffers | |
2548 | Visiting Files]]. The command ~magit-cherry~ also shows a log, see | |
2549 | [[*Cherries]]. | |
2541 | 2550 | |
2542 | 2551 | *** Refreshing Logs |
2543 | 2552 | |
2949 | 2958 | Show all diffs of a stash in a buffer. |
2950 | 2959 | |
2951 | 2960 | Two additional commands that show the diff for the file or blob that |
2952 | is being visited in the current buffer exists, see [[*Minor Mode for | |
2961 | is being visited in the current buffer exists, see [[*Commands for | |
2953 | 2962 | Buffers Visiting Files]]. |
2954 | 2963 | |
2955 | 2964 | *** Refreshing Diffs |
3220 | 3229 | hunk-internal region only lose their distinct background color or |
3221 | 3230 | also the foreground color. Whether the outside of the region is |
3222 | 3231 | dimmed at all depends on ~magit-diff-highlight-hunk-region-functions~. |
3232 | ||
3233 | - User Option: magit-diff-extra-stat-arguments | |
3234 | ||
3235 | This option specifies additional arguments to be used alongside | |
3236 | ~--stat~. | |
3237 | ||
3238 | The value is a list of zero or more arguments or a function that | |
3239 | takes no argument and returns such a list. These arguments are | |
3240 | allowed here: ~--stat-width~, ~--stat-name-width~, | |
3241 | ~--stat-graph-width~ and ~--compact-summary~. Also see [[man:git-diff]] | |
3223 | 3242 | |
3224 | 3243 | *** Revision Buffer |
3225 | 3244 | |
3660 | 3679 | |
3661 | 3680 | Bisecting a bug means to find the commit that introduced it. |
3662 | 3681 | This command starts such a bisect session by asking for a known |
3663 | good and a bad commit. | |
3682 | good commit and a known bad commit. If you're bisecting a change | |
3683 | that isn't a regression, you can select alternate terms that are | |
3684 | conceptually more fitting than "bad" and "good", but the infix | |
3685 | arguments to do so are disabled by default. | |
3664 | 3686 | |
3665 | 3687 | - Key: B s, magit-bisect-run |
3666 | 3688 | |
3678 | 3700 | |
3679 | 3701 | Mark the current commit as good. Use this after you have asserted |
3680 | 3702 | that the commit does not contain the bug in question. |
3703 | ||
3704 | - Key: B m, magit-bisect-mark | |
3705 | ||
3706 | Mark the current commit with one of the bisect terms. This command | |
3707 | provides an alternative to ~magit-bisect-bad~ and | |
3708 | ~magit-bisect-good~ and is useful when using terms other than "bad" | |
3709 | and "good". This suffix is disabled by default. | |
3681 | 3710 | |
3682 | 3711 | - Key: B k, magit-bisect-skip |
3683 | 3712 | |
3807 | 3836 | Also see [[man:git-blame]] |
3808 | 3837 | |
3809 | 3838 | To start blaming invoke the ~magit-file-dispatch~ transient prefix |
3810 | command by pressing ~C-c M-g~. (This is only the default binding and | |
3811 | the recommended binding is ~C-c g~. Also neither binding may be | |
3812 | available if you disabled ~global-magit-file-mode~. Also see [[*Minor | |
3813 | Mode for Buffers Visiting Files]].) | |
3839 | command by pressing ~C-c M-g~. | |
3814 | 3840 | |
3815 | 3841 | The blaming suffix commands can be invoked from the dispatch |
3816 | 3842 | transient. However if you want to set an infix argument, then you |
4350 | 4376 | Whether to ask to stage all unstaged changes when committing and nothing is |
4351 | 4377 | staged. |
4352 | 4378 | |
4379 | - User Option: magit-commit-show-diff | |
4380 | ||
4381 | Whether the relevant diff is automatically shown when committing. | |
4382 | ||
4353 | 4383 | - User Option: magit-commit-extend-override-date |
4354 | 4384 | |
4355 | 4385 | Whether using ~magit-commit-extend~ changes the committer date. |
4366 | 4396 | ~magit-commit-squash~ and ~magit-commit-fixup~. The "instant" variants |
4367 | 4397 | always require confirmation because making an error while using |
4368 | 4398 | those is harder to recover from. |
4399 | ||
4400 | - User Option magit-post-commit-hook | |
4401 | ||
4402 | Hook run after creating a commit without the user editing a message. | |
4403 | ||
4404 | This hook is run by ~magit-refresh~ if ~this-command~ is a member | |
4405 | of ~magit-post-stage-hook-commands~. This only includes commands | |
4406 | named ~magit-commit-*~ that do *not* require that the user edits | |
4407 | the commit message in a buffer. | |
4408 | ||
4409 | Also see ~git-commit-post-finish-hook~. | |
4369 | 4410 | |
4370 | 4411 | *** Editing Commit Messages |
4371 | 4412 | |
4722 | 4763 | - User Option: magit-branch-direct-configure |
4723 | 4764 | |
4724 | 4765 | This option controls whether the transient command ~magit-branch~ can |
4725 | be used directly change the values Git variables. This defaults to | |
4726 | ~t~ (to avoid changing key bindings). When set to ~nil~, then no | |
4766 | be used to directly change the values of Git variables. This defaults | |
4767 | to ~t~ (to avoid changing key bindings). When set to ~nil~, then no | |
4727 | 4768 | variables are displayed by that transient command, and its suffix |
4728 | 4769 | command ~magit-branch-configure~ has to be used instead to view and |
4729 | 4770 | change branch related variables. |
4768 | 4809 | |
4769 | 4810 | - Key: b c, magit-branch-and-checkout |
4770 | 4811 | |
4771 | This command creates a new branch like ~magit-branch~, but then also | |
4772 | checks it out. | |
4812 | This command creates a new branch like ~magit-branch-create~, but then | |
4813 | also checks it out. | |
4773 | 4814 | |
4774 | 4815 | Also see option ~magit-branch-prefer-remote-upstream~. |
4775 | 4816 | |
4861 | 4902 | - User Option: magit-branch-read-upstream-first |
4862 | 4903 | |
4863 | 4904 | When creating a branch, whether to read the upstream branch before |
4864 | the name of the branch that is to be created. The default is ~nil~, | |
4905 | the name of the branch that is to be created. The default is ~t~, | |
4865 | 4906 | and I recommend you leave it at that. |
4866 | 4907 | |
4867 | 4908 | - User Option: magit-branch-prefer-remote-upstream |
5935 | 5976 | |
5936 | 5977 | Reset the ~HEAD~, index, and working tree to some commit read from the |
5937 | 5978 | user and defaulting to the commit at point. |
5979 | ||
5980 | - Key: X k, magit-reset-keep | |
5981 | ||
5982 | Reset the ~HEAD~, index, and working tree to some commit read from the | |
5983 | user and defaulting to the commit at point. Uncommitted changes are | |
5984 | kept as-is. | |
5938 | 5985 | |
5939 | 5986 | - Key: X i, magit-reset-index |
5940 | 5987 | |
6375 | 6422 | |
6376 | 6423 | This command pushes a tag to another repository. |
6377 | 6424 | |
6425 | One of the infix arguments, ~--force-with-lease~, deserves a word of | |
6426 | caution. It is passed without a value, which means "permit a force | |
6427 | push as long as the remote-tracking branches match their counterparts | |
6428 | on the remote end". If you've set up a tool to do automatic fetches | |
6429 | (Magit itself does not provide such functionality), using | |
6430 | ~--force-with-lease~ can be dangerous because you don't actually | |
6431 | control or know the state of the remote-tracking refs. In that case, | |
6432 | you should consider setting ~push.useForceIfIncludes~ to ~true~ | |
6433 | (available since Git 2.30). | |
6434 | ||
6378 | 6435 | Two more push commands exist, which by default are not available from |
6379 | 6436 | the push transient. See their doc-strings for instructions on how to |
6380 | 6437 | add them to the transient. |
6390 | 6447 | ~branch.<branch>.remote~, ~branch.<branch>.merge~, and |
6391 | 6448 | ~remote.<remote>.push~. |
6392 | 6449 | |
6450 | If you add this suffix to a transient prefix without explicitly | |
6451 | specifying the description, then an attempt is made to predict | |
6452 | what this command will do. For example: | |
6453 | ||
6454 | #+BEGIN_SRC emacs-lisp | |
6455 | (transient-insert-suffix 'magit-push \"p\" | |
6456 | '(\"i\" magit-push-implicitly))" | |
6457 | #+END_SRC | |
6458 | ||
6393 | 6459 | - Command: magit-push-to-remote remote args |
6394 | 6460 | |
6395 | 6461 | This command pushes to the remote REMOTE without using an explicit |
6494 | 6560 | - Key: t t, magit-tag-create |
6495 | 6561 | |
6496 | 6562 | This command creates a new tag with the given NAME at REV. With a |
6497 | prefix argument it creates an annotate tag. | |
6563 | prefix argument it creates an annotated tag. | |
6498 | 6564 | |
6499 | 6565 | - Key: t r, magit-tag-release |
6500 | 6566 | |
6501 | This commands creates an annotated release tag. It assumes that | |
6502 | release tags match ~magit-release-tag-regexp~. | |
6567 | This commands creates a release tag. It assumes that release tags | |
6568 | match ~magit-release-tag-regexp~. | |
6503 | 6569 | |
6504 | 6570 | First it prompts for the name of the new tag using the highest |
6505 | 6571 | existing tag as initial input and leaving it to the user to |
6508 | 6574 | ~v1.2.3-custom.1~), you can set the ~magit-release-tag-regexp~ and |
6509 | 6575 | ~magit-tag-version-regexp-alist~ variables. |
6510 | 6576 | |
6511 | Then it prompts for the message of the new tag. The proposed tag | |
6512 | message is based on the message of the highest tag, provided that | |
6513 | that contains the corresponding version string and substituting the | |
6514 | new version string for that. Otherwise it proposes something like | |
6515 | "Foo-Bar 1.2.3", given, for example, a TAG "v1.2.3" and a repository | |
6516 | located at something like "/path/to/foo-bar". | |
6517 | ||
6518 | Then it calls "git tag --annotate --sign -m MSG TAG" to create the | |
6519 | tag, regardless of whether these arguments are enabled in the | |
6520 | transient. Finally it shows the refs buffer to let the user quickly | |
6521 | review the result. | |
6577 | If ~--annotate~ is enabled then it prompts for the message of the | |
6578 | new tag. The proposed tag message is based on the message of the | |
6579 | highest tag, provided that that contains the corresponding version | |
6580 | string and substituting the new version string for that. Otherwise | |
6581 | it proposes something like "Foo-Bar 1.2.3", given, for example, a | |
6582 | TAG "v1.2.3" and a repository located at something like | |
6583 | "/path/to/foo-bar". | |
6522 | 6584 | |
6523 | 6585 | - Key: t k, magit-tag-delete |
6524 | 6586 | |
6610 | 6672 | The command ~magit-list-submodules~ displays a list of the current |
6611 | 6673 | repository's submodules in a separate buffer. It's also possible to |
6612 | 6674 | display information about submodules directly in the status buffer of |
6613 | the super-repository by adding ~magit-insert-submodules~ to the hook | |
6675 | the super-repository by adding ~magit-insert-modules~ to the hook | |
6614 | 6676 | ~magit-status-sections-hook~ as described in [[*Status Module Sections]]. |
6615 | 6677 | |
6616 | 6678 | - Command: magit-list-submodules |
6633 | 6695 | ~default-directory~ bound to the toplevel of its working tree. It |
6634 | 6696 | has to return a string to be inserted or nil. PROPS is an alist |
6635 | 6697 | that supports the keys ~:right-align~ and ~:pad-right~. |
6636 | ||
6637 | - Function: magit-insert-submodules | |
6638 | ||
6639 | Insert sections for all submodules. For each section insert the | |
6640 | path, the branch, and the output of ~git describe --tags~, | |
6641 | or, failing that, the abbreviated HEAD commit hash. | |
6642 | ||
6643 | Press ~RET~ on such a submodule section to show its own status buffer. | |
6644 | Press ~RET~ on the "Modules" section to display a list of submodules | |
6645 | in a separate buffer. This shows additional information not | |
6646 | displayed in the super-repository's status buffer. | |
6647 | 6698 | |
6648 | 6699 | *** Submodule Transient |
6649 | 6700 | |
6805 | 6856 | major-modes derive from ~magit-mode~. There are other common commands |
6806 | 6857 | beside the ones below, but these didn't fit well anywhere else. |
6807 | 6858 | |
6808 | - Key: M-w, magit-copy-section-value | |
6859 | - Key: C-w, magit-copy-section-value | |
6809 | 6860 | |
6810 | 6861 | This command saves the value of the current section to the |
6811 | 6862 | ~kill-ring~, and, provided that the current section is a commit, |
6818 | 6869 | |
6819 | 6870 | When the region is active, this command saves that to the |
6820 | 6871 | ~kill-ring~, like ~kill-ring-save~ would, instead of behaving as |
6821 | described above. If a prefix argument is used and the region is | |
6822 | within a hunk, it strips the outer diff marker column before saving | |
6823 | the text. | |
6824 | ||
6825 | - Key: C-w, magit-copy-buffer-revision | |
6872 | described above. If a prefix argument is used and the region is | |
6873 | within a hunk, then it strips the diff marker column and keeps | |
6874 | only either the added or removed lines, depending on the sign of | |
6875 | the prefix argument. | |
6876 | ||
6877 | - Key: M-w, magit-copy-buffer-revision | |
6826 | 6878 | |
6827 | 6879 | This command saves the revision being displayed in the current buffer |
6828 | 6880 | to the ~kill-ring~ and also pushes it to the ~magit-revision-stack~. It |
7073 | 7125 | |
7074 | 7126 | Mode-line lighter for ~magit-wip-initial-backup-mode~. |
7075 | 7127 | |
7076 | ** Minor Mode for Buffers Visiting Files | |
7077 | ||
7078 | The minor-mode ~magit-file-mode~ enables certain Magit features in | |
7079 | file-visiting buffers belonging to a Git repository. The globalized | |
7080 | variant ~global-magit-file-mode~ enables the local mode in all such | |
7081 | buffers. It is enabled by default. Currently the local mode only | |
7082 | establishes a few key bindings, but this might be extended in the | |
7083 | future. | |
7084 | ||
7085 | - User Option: global-magit-file-mode | |
7086 | ||
7087 | Whether to establish certain Magit key bindings in all file-visiting | |
7088 | buffers belonging to any Git repository. This is enabled by default. | |
7089 | This globalized mode turns on the local minor-mode ~magit-file-mode~ | |
7090 | in all suitable buffers. | |
7091 | ||
7092 | - Variable: magit-file-mode-map | |
7093 | ||
7094 | This keymap is used by the local minor-mode ~magit-file-mode~ and | |
7095 | establishes the key bindings described below. | |
7096 | ||
7097 | Note that the default binding for ~magit-file-dispatch~ is very | |
7098 | cumbersome to use and that we recommend that you add a better | |
7099 | binding. | |
7100 | ||
7101 | Instead of ~C-c M-g~ I would have preferred to use ~C-c g~ because (1) | |
7102 | it is similar to ~C-x g~ (the recommended global binding for | |
7103 | ~~magit-status~), (2) we cannot use ~C-c C-g~ because we have been | |
7104 | recommending that that be bound to ~magit-dispatch~ for a long time, | |
7105 | (3) we cannot use ~C-x C-g~ because that is a convenient way of | |
7106 | aborting the incomplete key sequence ~C-x~, and most importantly (4) | |
7107 | it would make it much easier to type the next key (a suffix binding) | |
7108 | because most of those are letters. | |
7109 | ||
7110 | For example ~C-c g b~ is much easier to type than ~C-c M-g b~. For | |
7111 | suffix bindings that use uppercase letters, the default is just | |
7112 | horrible—having to use e.g. ~C-c M-g B~ (~Control+c Meta+g Shift+b~) | |
7113 | would drive anyone up the walls (or to Vim). | |
7114 | ||
7115 | However ~C-c LETTER~ bindings are reserved for users (see | |
7116 | [[info:elisp#Key Binding Conventions]]). Packages are forbidden from | |
7117 | using those. Doing so anyway is considered heresy. Therefore if | |
7118 | you want a better binding, you have to add it yourself: | |
7119 | ||
7120 | #+BEGIN_SRC emacs-lisp | |
7121 | (define-key magit-file-mode-map | |
7122 | (kbd "C-c g") 'magit-file-dispatch) | |
7123 | #+END_SRC | |
7128 | ** Commands for Buffers Visiting Files | |
7129 | ||
7130 | Magit defines a few global key bindings unless the user sets | |
7131 | ~magit-define-global-key-bindings~ to ~nil~. This includes binding ~C-c | |
7132 | M-g~ to ~magit-file-dispatch~. ~C-c g~ would be a much better binding | |
7133 | but the ~C-c <letter>~ namespace is reserved for users, meaning that | |
7134 | packages are not allowed to use it. If you want to use ~C-c g~, then | |
7135 | you have to add that binding yourself. Also see [[*Default Bindings]] | |
7136 | and [[info:elisp#Key Binding Conventions]]. | |
7137 | ||
7138 | If you want a better binding, you have to add it yourself: | |
7139 | ||
7140 | #+BEGIN_SRC emacs-lisp | |
7141 | (global-set-key (kbd "C-c g") 'magit-file-dispatch) | |
7142 | #+END_SRC | |
7124 | 7143 | |
7125 | 7144 | The key bindings shown below assume that you have not improved the |
7126 | 7145 | binding for ~magit-file-dispatch~. |
7129 | 7148 | |
7130 | 7149 | This transient prefix command binds the following suffix commands |
7131 | 7150 | and displays them in a temporary buffer until a suffix is invoked. |
7151 | ||
7152 | When invoked in a buffer that does not visit a file, then it falls | |
7153 | back to regular ~magit-dispatch~. | |
7132 | 7154 | |
7133 | 7155 | - Key: C-c M-g s, magit-stage-file |
7134 | 7156 | |
7568 | 7590 | the diff, which is useful because it increases the odds that you spot |
7569 | 7591 | potential issues. |
7570 | 7592 | |
7571 | **** The Built-In VC Package | |
7572 | :PROPERTIES: | |
7573 | :NONODE: t | |
7574 | :END: | |
7575 | ||
7576 | Emacs comes with a version control interface called "VC", see | |
7577 | [[info:emacs#Version Control]]. It is enabled be default, and if you don't | |
7578 | use it in addition to Magit, then you should disable it to keep it | |
7579 | from performing unnecessary work: | |
7580 | ||
7581 | #+BEGIN_SRC emacs-lisp | |
7582 | (setq vc-handled-backends nil) | |
7583 | #+END_SRC | |
7584 | ||
7585 | You can also disable its use for Git but keep using it when using | |
7586 | another version control system: | |
7587 | ||
7588 | #+BEGIN_SRC emacs-lisp | |
7589 | (setq vc-handled-backends (delq 'Git vc-handled-backends)) | |
7590 | #+END_SRC | |
7591 | ||
7592 | 7593 | **** Microsoft Windows Performance |
7593 | 7594 | |
7594 | 7595 | In order to update the status buffer, ~git~ has to be run a few dozen |
7631 | 7632 | |
7632 | 7633 | On Catalina, and potentially other macOS releases, there may be a |
7633 | 7634 | performance problem where any action takes 20 times longer on Darwin |
7634 | than on Linux. This can be fixed by setting ~magit-git-executable~ to | |
7635 | the absolute path of the ~git~ executable, instead of relying on | |
7636 | resolving the ~$PATH~. | |
7635 | than on Linux. This can be worked around by setting | |
7636 | ~magit-git-executable~ to the absolute path of the ~git~ executable, | |
7637 | instead of relying on resolving the ~$PATH~. You should not do that if | |
7638 | you want to use Magit on remote machines using Tramp and if ~git~ is not | |
7639 | installed in the same location on those machines. | |
7637 | 7640 | |
7638 | 7641 | [fn:mac1] https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-04/msg00201.html |
7642 | ||
7643 | *** Default Bindings | |
7644 | ||
7645 | - User Option: magit-define-global-key-bindings | |
7646 | ||
7647 | This option controls whether some Magit commands are automatically | |
7648 | bound in the global keymap even before Magit is used for the first | |
7649 | time in the current session. | |
7650 | ||
7651 | If this variable is non-nil, which it is by default, then the | |
7652 | following bindings may be added to the global keymap. | |
7653 | ||
7654 | | ~C-x g~ | ~magit-status~ | | |
7655 | | ~C-x M-g~ | ~magit-dispatch~ | | |
7656 | | ~C-c M-g~ | ~magit-file-dispatch~ | | |
7657 | ||
7658 | These bindings may be added when ~after-init-hook~ is called. | |
7659 | Each binding is added if and only if at that time no other key | |
7660 | is bound to the same command and no other command is bound to | |
7661 | the same key. In other words we try to avoid adding bindings | |
7662 | that are unnecessary, as well as bindings that conflict with | |
7663 | other bindings. | |
7664 | ||
7665 | Adding the above bindings is delayed until ~after-init-hook~ | |
7666 | is called to allow users to set the variable anywhere in their | |
7667 | init file (without having to make sure to do so before ~magit~ | |
7668 | is loaded or autoloaded) and to increase the likelihood that | |
7669 | all the potentially conflicting user bindings have already | |
7670 | been added. | |
7671 | ||
7672 | Setting this variable after the hook has already been called | |
7673 | has no effect. | |
7674 | ||
7675 | We recommend that you bind ~C-c g~ instead of ~C-c M-g~ to | |
7676 | ~magit-file-dispatch~. The former is a much better binding | |
7677 | but the ~C-c <letter>~ namespace is strictly reserved for | |
7678 | users; preventing Magit from using it by default. | |
7679 | ||
7680 | #+BEGIN_SRC emacs-lisp | |
7681 | (global-set-key (kbd "C-c g") 'magit-file-dispatch) | |
7682 | #+END_SRC | |
7683 | ||
7684 | Also see [[*Commands for Buffers Visiting Files]] and [[info:elisp#Key | |
7685 | Binding Conventions]]. | |
7639 | 7686 | |
7640 | 7687 | * Plumbing |
7641 | 7688 | ** _ :ignore: |
7792 | 7839 | |
7793 | 7840 | Calls git synchronously with ARGS and then refreshes. |
7794 | 7841 | |
7795 | - Function: magit-run-git-with-input input &rest args | |
7796 | ||
7797 | Calls git synchronously with ARGS and sends it INPUT on standard | |
7798 | input. | |
7799 | ||
7800 | INPUT should be a buffer or the name of an existing buffer. The | |
7801 | content of that buffer is used as the process' standard input. | |
7802 | After the process returns a refresh is performed. | |
7803 | ||
7804 | As a special case, INPUT may also be nil. In that case the content | |
7805 | of the current buffer is used as standard input and *no* refresh is | |
7806 | performed. | |
7807 | ||
7808 | This function actually runs git asynchronously. But then it waits | |
7809 | for the process to return, so the function itself is synchronous. | |
7842 | - Function: magit-run-git-with-input &rest args | |
7843 | ||
7844 | Calls git synchronously with ARGS and sends it the content of the | |
7845 | current buffer on standard input. | |
7846 | ||
7847 | If the current buffer's ~default-directory~ is on a remote | |
7848 | filesystem, this function actually runs git asynchronously. But | |
7849 | then it waits for the process to return, so the function itself is | |
7850 | synchronous. | |
7810 | 7851 | |
7811 | 7852 | - Function: magit-run-git-with-logfile file &rest args |
7812 | 7853 | |
7813 | Calls git synchronously with ARGS. The process' output is saved in | |
7854 | Calls git synchronously with ARGS. The process's output is saved in | |
7814 | 7855 | FILE. This is rarely useful and so this function might be removed |
7815 | 7856 | in the future. |
7816 | 7857 | |
7857 | 7898 | current when this function was called (if it is a Magit buffer and |
7858 | 7899 | still alive), as well as the respective Magit status buffer. |
7859 | 7900 | |
7860 | - Function: magit-start-git &rest args | |
7901 | - Function: magit-start-git input &rest args | |
7861 | 7902 | |
7862 | 7903 | Start Git, prepare for refresh, and return the process object. |
7863 | 7904 | |
8380 | 8421 | Please also use the [[*Debugging Tools]]. |
8381 | 8422 | |
8382 | 8423 | ** FAQ - How to ...? |
8424 | *** How to pronounce Magit? | |
8425 | ||
8426 | Either ~mu[m's] git~ or ~magi{c => t}~ is fine. | |
8427 | ||
8428 | The slogan is "It's Magit! The magical Git client", so it makes sense | |
8429 | to pronounce Magit like magic, while taking into account that C and T | |
8430 | do not sound the same. | |
8431 | ||
8432 | The German "Magie" is not pronounced the same as the English "magic", | |
8433 | so if you speak German then you can use the above rational to justify | |
8434 | using the former pronunciation; ~Mag{ie => it}~. | |
8435 | ||
8436 | You can also choose to use the former pronunciation just because you | |
8437 | like it better. | |
8438 | ||
8439 | Also see https://magit.vc/assets/videos/magic.mp4. | |
8440 | Also see https://emacs.stackexchange.com/questions/13696. | |
8441 | ||
8383 | 8442 | *** How to show git's output? |
8384 | 8443 | |
8385 | 8444 | To show the output of recently run git commands, press ~$~ (or, if that |
8459 | 8518 | command ~magit-ediff-resolve~ which only shows yet-to-be resolved |
8460 | 8519 | conflicts. |
8461 | 8520 | |
8521 | *** Should I disable VC? | |
8522 | ||
8523 | If you don't use VC (the built-in version control interface) then | |
8524 | you might be tempted to disable it, not least because we used to | |
8525 | recommend that you do that. | |
8526 | ||
8527 | We no longer recommend that you disable VC. Doing so would break | |
8528 | useful third-party packages (such as ~diff-hl~), which depend on VC | |
8529 | being enabled. | |
8530 | ||
8531 | If you choose to disable VC anyway, then you can do so by changing | |
8532 | the value of ~vc-handled-backends~. | |
8533 | ||
8462 | 8534 | ** FAQ - Issues and Errors |
8463 | 8535 | *** Magit is slow |
8464 | 8536 | |
8543 | 8615 | But doing so isn't good for performance. For more (overly optimistic) |
8544 | 8616 | information see [[info:emacs#VC Mode Line]]. |
8545 | 8617 | |
8546 | If you don't really care about seeing that information in the | |
8547 | mode-line, but just don't want to see /incorrect/ information, then | |
8548 | consider disabling VC when using Git: | |
8618 | If you don't really care about seeing this information in the | |
8619 | mode-line, but just don't want to see /incorrect/ information, | |
8620 | then consider simply not displaying it in the mode-line: | |
8549 | 8621 | |
8550 | 8622 | #+BEGIN_SRC emacs-lisp |
8551 | (setq vc-handled-backends (delq 'Git vc-handled-backends)) | |
8552 | #+END_SRC | |
8553 | ||
8554 | Or to disable it completely: | |
8555 | ||
8556 | #+BEGIN_SRC emacs-lisp | |
8557 | (setq vc-handled-backends nil) | |
8623 | (setq-default mode-line-format | |
8624 | (delete '(vc-mode vc-mode) mode-line-format)) | |
8558 | 8625 | #+END_SRC |
8559 | 8626 | |
8560 | 8627 | *** A branch and tag sharing the same name breaks SOMETHING |
8627 | 8694 | #+END_SRC |
8628 | 8695 | |
8629 | 8696 | This will actually end up using ~emacs~, not ~emacsclient~. If you do |
8630 | this, then can still edit the commit message but ~git-commit-mode~ won't | |
8631 | be used and you have to exit ~emacs~ to finish the process. | |
8697 | this, then you can still edit the commit message but ~git-commit-mode~ | |
8698 | won't be used and you have to exit ~emacs~ to finish the process. | |
8632 | 8699 | |
8633 | 8700 | Tautology ahead. If you want to be able to use ~emacsclient~ to connect |
8634 | 8701 | to a running ~emacs~ instance, even though no ~emacs~ instance is running, |
8729 | 8796 | :END: |
8730 | 8797 | |
8731 | 8798 | #+BEGIN_QUOTE |
8732 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@bernoul.li> | |
8799 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@bernoul.li> | |
8733 | 8800 | |
8734 | 8801 | You can redistribute this document and/or modify it under the terms |
8735 | 8802 | of the GNU General Public License as published by the Free Software |
7 | 7 | |
8 | 8 | @copying |
9 | 9 | @quotation |
10 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@@bernoul.li> | |
10 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@@bernoul.li> | |
11 | 11 | |
12 | 12 | You can redistribute this document and/or modify it under the terms |
13 | 13 | of the GNU General Public License as published by the Free Software |
30 | 30 | @finalout |
31 | 31 | @titlepage |
32 | 32 | @title Magit User Manual |
33 | @subtitle for version 2.90.1 (v2.90.1-954-g509e97b7+1) | |
33 | @subtitle for version 3.0.0 | |
34 | 34 | @author Jonas Bernoulli |
35 | 35 | @page |
36 | 36 | @vskip 0pt plus 1filll |
52 | 52 | Magit and Git itself deserve to be called porcelains. |
53 | 53 | |
54 | 54 | @noindent |
55 | This manual is for Magit version 2.90.1 (v2.90.1-954-g509e97b7+1). | |
55 | This manual is for Magit version 3.0.0. | |
56 | 56 | |
57 | 57 | @quotation |
58 | Copyright (C) 2015-2020 Jonas Bernoulli <jonas@@bernoul.li> | |
58 | Copyright (C) 2015-2021 Jonas Bernoulli <jonas@@bernoul.li> | |
59 | 59 | |
60 | 60 | You can redistribute this document and/or modify it under the terms |
61 | 61 | of the GNU General Public License as published by the Free Software |
262 | 262 | * Worktree:: |
263 | 263 | * Common Commands:: |
264 | 264 | * Wip Modes:: |
265 | * Minor Mode for Buffers Visiting Files:: | |
265 | * Commands for Buffers Visiting Files:: | |
266 | 266 | * Minor Mode for Buffers Visiting Blobs:: |
267 | 267 | |
268 | 268 | Submodules |
286 | 286 | |
287 | 287 | * Safety:: |
288 | 288 | * Performance:: |
289 | * Default Bindings:: | |
289 | 290 | |
290 | 291 | |
291 | 292 | Plumbing |
320 | 321 | |
321 | 322 | FAQ - How to @dots{}? |
322 | 323 | |
324 | * How to pronounce Magit?:: | |
323 | 325 | * How to show git's output?:: |
324 | 326 | * How to install the gitman info manual?:: |
325 | 327 | * How to show diffs for gpg-encrypted files?:: |
326 | 328 | * How does branching and pushing work?:: |
327 | 329 | * Can Magit be used as @code{ediff-version-control-package}?:: |
330 | * Should I disable VC@?:: | |
328 | 331 | |
329 | 332 | |
330 | 333 | FAQ - Issues and Errors |
511 | 514 | with the following content before running @code{make}: |
512 | 515 | |
513 | 516 | @example |
514 | LOAD_PATH = -L /path/to/magit/lisp | |
515 | LOAD_PATH += -L /path/to/dash | |
516 | LOAD_PATH += -L /path/to/transient | |
517 | LOAD_PATH += -L /path/to/with-editor | |
517 | LOAD_PATH = -L ~/.emacs.d/site-lisp/magit/lisp | |
518 | LOAD_PATH += -L ~/.emacs.d/site-lisp/dash | |
519 | LOAD_PATH += -L ~/.emacs.d/site-lisp/transient/lisp | |
520 | LOAD_PATH += -L ~/.emacs.d/site-lisp/with-editor | |
518 | 521 | @end example |
519 | 522 | |
520 | 523 | Finally add this to your init file: |
529 | 532 | "~/.emacs.d/site-lisp/magit/Documentation/")) |
530 | 533 | @end lisp |
531 | 534 | |
535 | Of course if you installed the dependencies manually as well, then | |
536 | you have to tell Emacs about them too, by prefixing the above with: | |
537 | ||
538 | @lisp | |
539 | (add-to-list 'load-path "~/.emacs.d/site-lisp/dash") | |
540 | (add-to-list 'load-path "~/.emacs.d/site-lisp/transient/lisp") | |
541 | (add-to-list 'load-path "~/.emacs.d/site-lisp/with-editor") | |
542 | @end lisp | |
543 | ||
532 | 544 | Note that you have to add the @code{lisp} subdirectory to the @code{load-path}, not |
533 | 545 | the top-level of the repository, and that elements of @code{load-path} should |
534 | 546 | not end with a slash, while those of @code{Info-directory-list} should. |
604 | 616 | if you do that, then you should commit all uncommitted changes before |
605 | 617 | proceeding. |
606 | 618 | |
607 | To display information about the current Git repository, type @code{M-x | |
608 | magit-status RET}. You will be using this command a lot, and should | |
609 | therefore give it a global key binding. This is what we recommend: | |
610 | ||
611 | @lisp | |
612 | (global-set-key (kbd "C-x g") 'magit-status) | |
613 | @end lisp | |
619 | Type @code{C-x g} to display information about the current Git repository in | |
620 | a dedicated buffer, called the status buffer. | |
614 | 621 | |
615 | 622 | Most Magit commands are commonly invoked from the status buffer. It |
616 | 623 | can be considered the primary interface for interacting with Git using |
662 | 669 | |
663 | 670 | Now two new buffers appear. One is for writing the commit message, |
664 | 671 | the other shows a diff with the changes that you are about to |
665 | committed. Write a message and then type @code{C-c C-c} to actually create | |
672 | commit. Write a message and then type @code{C-c C-c} to actually create | |
666 | 673 | the commit. |
667 | 674 | |
668 | 675 | You probably don't want to push the commit you just created because |
673 | 680 | push-remote is not configured yet, then you would first be prompted |
674 | 681 | for the remote to push to.) |
675 | 682 | |
676 | So far we have mentioned the commit, push, and log transient prefix | |
677 | commands. These are probably among the transients you will be using | |
678 | the most, but many others exist. To show a transient that lists all | |
679 | other transients (as well as the various apply commands and some other | |
680 | essential commands), type @code{h}. Try a few. | |
681 | ||
682 | The key bindings in that transient correspond to the bindings in Magit | |
683 | So far we have mentioned the commit, push, and log menu commands. | |
684 | These are probably among the menus you will be using the most, but | |
685 | many others exist. To show a menu that lists all other menus (as well | |
686 | as the various apply commands and some other essential commands), type | |
687 | @code{h}. Try a few. (Such menus are also called "transient prefix | |
688 | commands" or just "transients".) | |
689 | ||
690 | The key bindings in that menu correspond to the bindings in Magit | |
683 | 691 | buffers, including but not limited to the status buffer. So you could |
684 | type @code{h d} to bring up the diff transient, but once you remember that | |
685 | "d" stands for "diff", you would usually do so by just typing @code{d}. But | |
686 | this "prefix of prefixes" is useful even once you have memorized all | |
687 | the bindings, as it can provide easy access to Magit commands from | |
688 | non-Magit buffers. You should create a global key binding for this | |
689 | command too: | |
690 | ||
691 | @lisp | |
692 | (global-set-key (kbd "C-x M-g") 'magit-dispatch) | |
693 | @end lisp | |
694 | ||
695 | In the same vein, you might also want to enable @code{global-magit-file-mode} | |
696 | to get some more Magit key bindings in regular file-visiting buffers | |
697 | (see @ref{Minor Mode for Buffers Visiting Files}). | |
692 | type @code{h d} to bring up the diff menu, but once you remember that "d" | |
693 | stands for "diff", you would usually do so by just typing @code{d}. But this | |
694 | "prefix of prefixes" is useful even once you have memorized all the | |
695 | bindings, as it can provide easy access to Magit commands from | |
696 | non-Magit buffers. The global binding is @code{C-x M-g}. | |
697 | ||
698 | In file visiting buffers @code{C-c M-g} brings up a similar menu featuring | |
699 | commands that act on just the visited file, see @ref{Commands for Buffers Visiting Files}. | |
698 | 700 | |
699 | 701 | It is not necessary that you do so now, but if you stick with Magit, |
700 | 702 | then it is highly recommended that you read the next section too. |
1034 | 1036 | by adding a hook, like so: |
1035 | 1037 | |
1036 | 1038 | @lisp |
1037 | (add-hook 'after-save-hook 'magit-after-save-refresh-status t) | |
1039 | (with-eval-after-load 'magit-mode | |
1040 | (add-hook 'after-save-hook 'magit-after-save-refresh-status t)) | |
1038 | 1041 | @end lisp |
1039 | 1042 | |
1040 | 1043 | Automatically refreshing Magit buffers ensures that the displayed |
2066 | 2069 | @item |
2067 | 2070 | @code{delete-pr-remote} When deleting a branch that was created from a |
2068 | 2071 | pull-request and if no other branches still exist on that |
2069 | remote, then `magit-branch-delete' offers to delete the remote | |
2072 | remote, then @code{magit-branch-delete} offers to delete the remote | |
2070 | 2073 | as well. This should be safe because it only happens if no |
2071 | 2074 | other refs exist in the remotes namespace, and you can recreate |
2072 | 2075 | the remote if necessary. |
2080 | 2083 | to confirm by accepting the default (or selecting another). |
2081 | 2084 | This action only concerns the deletion of multiple stashes at |
2082 | 2085 | once. |
2086 | @end itemize | |
2087 | ||
2088 | ||
2089 | @item | |
2090 | Publishing: | |
2091 | ||
2092 | @itemize | |
2093 | @item | |
2094 | @code{set-and-push} When pushing to the upstream or the push-remote | |
2095 | and that isn't actually configured yet, then the user can first | |
2096 | set the target. If s/he confirms the default too quickly, then | |
2097 | s/he might end up pushing to the wrong branch and if the remote | |
2098 | repository is configured to disallow fixing such mistakes, then | |
2099 | that can be quite embarrassing and annoying. | |
2083 | 2100 | @end itemize |
2084 | 2101 | |
2085 | 2102 | |
3390 | 3407 | Show log for all references and @code{HEAD}. |
3391 | 3408 | @end table |
3392 | 3409 | |
3393 | ||
3394 | Two additional commands that show the log for the file or blob that | |
3395 | is being visited in the current buffer exists, see @ref{Minor Mode for Buffers Visiting Files}. The command @code{magit-cherry} also shows a log, | |
3396 | see @ref{Cherries}. | |
3410 | Two additional commands that show the log for the file or blob that is | |
3411 | being visited in the current buffer exists, see @ref{Commands for Buffers Visiting Files}. The command @code{magit-cherry} also shows a log, see | |
3412 | @ref{Cherries}. | |
3397 | 3413 | |
3398 | 3414 | @menu |
3399 | 3415 | * Refreshing Logs:: |
3994 | 4010 | @end table |
3995 | 4011 | |
3996 | 4012 | Two additional commands that show the diff for the file or blob that |
3997 | is being visited in the current buffer exists, see @ref{Minor Mode for Buffers Visiting Files}. | |
4013 | is being visited in the current buffer exists, see @ref{Commands for Buffers Visiting Files}. | |
3998 | 4014 | |
3999 | 4015 | @menu |
4000 | 4016 | * Refreshing Diffs:: |
4364 | 4380 | hunk-internal region only lose their distinct background color or |
4365 | 4381 | also the foreground color. Whether the outside of the region is |
4366 | 4382 | dimmed at all depends on @code{magit-diff-highlight-hunk-region-functions}. |
4383 | @end defopt | |
4384 | ||
4385 | @defopt magit-diff-extra-stat-arguments | |
4386 | ||
4387 | This option specifies additional arguments to be used alongside | |
4388 | @code{--stat}. | |
4389 | ||
4390 | The value is a list of zero or more arguments or a function that | |
4391 | takes no argument and returns such a list. These arguments are | |
4392 | allowed here: @code{--stat-width}, @code{--stat-name-width}, | |
4393 | @code{--stat-graph-width} and @code{--compact-summary}. Also see | |
4394 | @ifinfo | |
4395 | @ref{git-diff,,,gitman,}. | |
4396 | @end ifinfo | |
4397 | @ifhtml | |
4398 | @html | |
4399 | the <a href="http://git-scm.com/docs/git-diff">git-diff(1)</a> manpage. | |
4400 | @end html | |
4401 | @end ifhtml | |
4402 | @iftex | |
4403 | the git-diff(1) manpage. | |
4404 | @end iftex | |
4367 | 4405 | @end defopt |
4368 | 4406 | |
4369 | 4407 | @node Revision Buffer |
4956 | 4994 | |
4957 | 4995 | Bisecting a bug means to find the commit that introduced it. |
4958 | 4996 | This command starts such a bisect session by asking for a known |
4959 | good and a bad commit. | |
4997 | good commit and a known bad commit. If you're bisecting a change | |
4998 | that isn't a regression, you can select alternate terms that are | |
4999 | conceptually more fitting than "bad" and "good", but the infix | |
5000 | arguments to do so are disabled by default. | |
4960 | 5001 | |
4961 | 5002 | @kindex B s |
4962 | 5003 | @cindex magit-bisect-run |
4982 | 5023 | |
4983 | 5024 | Mark the current commit as good. Use this after you have asserted |
4984 | 5025 | that the commit does not contain the bug in question. |
5026 | ||
5027 | @kindex B m | |
5028 | @cindex magit-bisect-mark | |
5029 | @item @kbd{B m} @tie{}@tie{}@tie{}@tie{}(@code{magit-bisect-mark}) | |
5030 | ||
5031 | Mark the current commit with one of the bisect terms. This command | |
5032 | provides an alternative to @code{magit-bisect-bad} and | |
5033 | @code{magit-bisect-good} and is useful when using terms other than "bad" | |
5034 | and "good". This suffix is disabled by default. | |
4985 | 5035 | |
4986 | 5036 | @kindex B k |
4987 | 5037 | @cindex magit-bisect-skip |
5168 | 5218 | @end iftex |
5169 | 5219 | |
5170 | 5220 | To start blaming invoke the @code{magit-file-dispatch} transient prefix |
5171 | command by pressing @code{C-c M-g}. (This is only the default binding and | |
5172 | the recommended binding is @code{C-c g}. Also neither binding may be | |
5173 | available if you disabled @code{global-magit-file-mode}. Also see @ref{Minor Mode for Buffers Visiting Files}.) | |
5221 | command by pressing @code{C-c M-g}. | |
5174 | 5222 | |
5175 | 5223 | The blaming suffix commands can be invoked from the dispatch |
5176 | 5224 | transient. However if you want to set an infix argument, then you |
5927 | 5975 | staged. |
5928 | 5976 | @end defopt |
5929 | 5977 | |
5978 | @defopt magit-commit-show-diff | |
5979 | ||
5980 | Whether the relevant diff is automatically shown when committing. | |
5981 | @end defopt | |
5982 | ||
5930 | 5983 | @defopt magit-commit-extend-override-date |
5931 | 5984 | |
5932 | 5985 | Whether using @code{magit-commit-extend} changes the committer date. |
5946 | 5999 | always require confirmation because making an error while using |
5947 | 6000 | those is harder to recover from. |
5948 | 6001 | @end defopt |
6002 | ||
6003 | @itemize | |
6004 | @item | |
6005 | User Option magit-post-commit-hook | |
6006 | ||
6007 | Hook run after creating a commit without the user editing a message. | |
6008 | ||
6009 | This hook is run by @code{magit-refresh} if @code{this-command} is a member | |
6010 | of @code{magit-post-stage-hook-commands}. This only includes commands | |
6011 | named @code{magit-commit-*} that do @strong{not} require that the user edits | |
6012 | the commit message in a buffer. | |
6013 | ||
6014 | Also see @code{git-commit-post-finish-hook}. | |
6015 | @end itemize | |
5949 | 6016 | |
5950 | 6017 | @node Editing Commit Messages |
5951 | 6018 | @subsection Editing Commit Messages |
6388 | 6455 | @defopt magit-branch-direct-configure |
6389 | 6456 | |
6390 | 6457 | This option controls whether the transient command @code{magit-branch} can |
6391 | be used directly change the values Git variables. This defaults to | |
6392 | @code{t} (to avoid changing key bindings). When set to @code{nil}, then no | |
6458 | be used to directly change the values of Git variables. This defaults | |
6459 | to @code{t} (to avoid changing key bindings). When set to @code{nil}, then no | |
6393 | 6460 | variables are displayed by that transient command, and its suffix |
6394 | 6461 | command @code{magit-branch-configure} has to be used instead to view and |
6395 | 6462 | change branch related variables. |
6452 | 6519 | @cindex magit-branch-and-checkout |
6453 | 6520 | @item @kbd{b c} @tie{}@tie{}@tie{}@tie{}(@code{magit-branch-and-checkout}) |
6454 | 6521 | |
6455 | This command creates a new branch like @code{magit-branch}, but then also | |
6456 | checks it out. | |
6522 | This command creates a new branch like @code{magit-branch-create}, but then | |
6523 | also checks it out. | |
6457 | 6524 | |
6458 | 6525 | Also see option @code{magit-branch-prefer-remote-upstream}. |
6459 | 6526 | |
6566 | 6633 | @defopt magit-branch-read-upstream-first |
6567 | 6634 | |
6568 | 6635 | When creating a branch, whether to read the upstream branch before |
6569 | the name of the branch that is to be created. The default is @code{nil}, | |
6636 | the name of the branch that is to be created. The default is @code{t}, | |
6570 | 6637 | and I recommend you leave it at that. |
6571 | 6638 | @end defopt |
6572 | 6639 | |
8007 | 8074 | Reset the @code{HEAD}, index, and working tree to some commit read from the |
8008 | 8075 | user and defaulting to the commit at point. |
8009 | 8076 | |
8077 | @kindex X k | |
8078 | @cindex magit-reset-keep | |
8079 | @item @kbd{X k} @tie{}@tie{}@tie{}@tie{}(@code{magit-reset-keep}) | |
8080 | ||
8081 | Reset the @code{HEAD}, index, and working tree to some commit read from the | |
8082 | user and defaulting to the commit at point. Uncommitted changes are | |
8083 | kept as-is. | |
8084 | ||
8010 | 8085 | @kindex X i |
8011 | 8086 | @cindex magit-reset-index |
8012 | 8087 | @item @kbd{X i} @tie{}@tie{}@tie{}@tie{}(@code{magit-reset-index}) |
8665 | 8740 | This command pushes a tag to another repository. |
8666 | 8741 | @end table |
8667 | 8742 | |
8743 | One of the infix arguments, @code{--force-with-lease}, deserves a word of | |
8744 | caution. It is passed without a value, which means "permit a force | |
8745 | push as long as the remote-tracking branches match their counterparts | |
8746 | on the remote end". If you've set up a tool to do automatic fetches | |
8747 | (Magit itself does not provide such functionality), using | |
8748 | @code{--force-with-lease} can be dangerous because you don't actually | |
8749 | control or know the state of the remote-tracking refs. In that case, | |
8750 | you should consider setting @code{push.useForceIfIncludes} to @code{true} | |
8751 | (available since Git 2.30). | |
8752 | ||
8668 | 8753 | Two more push commands exist, which by default are not available from |
8669 | 8754 | the push transient. See their doc-strings for instructions on how to |
8670 | 8755 | add them to the transient. |
8680 | 8765 | @code{remote.pushDefault}, @code{branch.<branch>.pushRemote}, |
8681 | 8766 | @code{branch.<branch>.remote}, @code{branch.<branch>.merge}, and |
8682 | 8767 | @code{remote.<remote>.push}. |
8768 | ||
8769 | If you add this suffix to a transient prefix without explicitly | |
8770 | specifying the description, then an attempt is made to predict | |
8771 | what this command will do. For example: | |
8772 | ||
8773 | @lisp | |
8774 | (transient-insert-suffix 'magit-push \"p\" | |
8775 | '(\"i\" magit-push-implicitly))" | |
8776 | @end lisp | |
8683 | 8777 | @end deffn |
8684 | 8778 | |
8685 | 8779 | @cindex magit-push-to-remote remote args |
8838 | 8932 | * Worktree:: |
8839 | 8933 | * Common Commands:: |
8840 | 8934 | * Wip Modes:: |
8841 | * Minor Mode for Buffers Visiting Files:: | |
8935 | * Commands for Buffers Visiting Files:: | |
8842 | 8936 | * Minor Mode for Buffers Visiting Blobs:: |
8843 | 8937 | @end menu |
8844 | 8938 | |
8872 | 8966 | @item @kbd{t t} @tie{}@tie{}@tie{}@tie{}(@code{magit-tag-create}) |
8873 | 8967 | |
8874 | 8968 | This command creates a new tag with the given NAME at REV@. With a |
8875 | prefix argument it creates an annotate tag. | |
8969 | prefix argument it creates an annotated tag. | |
8876 | 8970 | |
8877 | 8971 | @kindex t r |
8878 | 8972 | @cindex magit-tag-release |
8879 | 8973 | @item @kbd{t r} @tie{}@tie{}@tie{}@tie{}(@code{magit-tag-release}) |
8880 | 8974 | |
8881 | This commands creates an annotated release tag. It assumes that | |
8882 | release tags match @code{magit-release-tag-regexp}. | |
8975 | This commands creates a release tag. It assumes that release tags | |
8976 | match @code{magit-release-tag-regexp}. | |
8883 | 8977 | |
8884 | 8978 | First it prompts for the name of the new tag using the highest |
8885 | 8979 | existing tag as initial input and leaving it to the user to |
8886 | increment the desired part of the version string. | |
8887 | ||
8888 | Then it prompts for the message of the new tag. The proposed tag | |
8889 | message is based on the message of the highest tag, provided that | |
8890 | that contains the corresponding version string and substituting the | |
8891 | new version string for that. Otherwise it proposes something like | |
8892 | "Foo-Bar 1.2.3", given, for example, a TAG "v1.2.3" and a repository | |
8893 | located at something like "/path/to/foo-bar". | |
8894 | ||
8895 | Then it calls "git tag --annotate --sign -m MSG TAG" to create the | |
8896 | tag, regardless of whether these arguments are enabled in the | |
8897 | transient. Finally it shows the refs buffer to let the user quickly | |
8898 | review the result. | |
8980 | increment the desired part of the version string. If you use | |
8981 | unconventional release tags or version numbers (e.g., | |
8982 | @code{v1.2.3-custom.1}), you can set the @code{magit-release-tag-regexp} and | |
8983 | @code{magit-tag-version-regexp-alist} variables. | |
8984 | ||
8985 | If @code{--annotate} is enabled then it prompts for the message of the | |
8986 | new tag. The proposed tag message is based on the message of the | |
8987 | highest tag, provided that that contains the corresponding version | |
8988 | string and substituting the new version string for that. Otherwise | |
8989 | it proposes something like "Foo-Bar 1.2.3", given, for example, a | |
8990 | TAG "v1.2.3" and a repository located at something like | |
8991 | "/path/to/foo-bar". | |
8899 | 8992 | |
8900 | 8993 | @kindex t k |
8901 | 8994 | @cindex magit-tag-delete |
9043 | 9136 | The command @code{magit-list-submodules} displays a list of the current |
9044 | 9137 | repository's submodules in a separate buffer. It's also possible to |
9045 | 9138 | display information about submodules directly in the status buffer of |
9046 | the super-repository by adding @code{magit-insert-submodules} to the hook | |
9139 | the super-repository by adding @code{magit-insert-modules} to the hook | |
9047 | 9140 | @code{magit-status-sections-hook} as described in @ref{Status Module Sections}. |
9048 | 9141 | |
9049 | 9142 | @cindex magit-list-submodules |
9069 | 9162 | has to return a string to be inserted or nil. PROPS is an alist |
9070 | 9163 | that supports the keys @code{:right-align} and @code{:pad-right}. |
9071 | 9164 | @end defopt |
9072 | ||
9073 | @defun magit-insert-submodules | |
9074 | ||
9075 | Insert sections for all submodules. For each section insert the | |
9076 | path, the branch, and the output of @code{git describe --tags}, | |
9077 | or, failing that, the abbreviated HEAD commit hash. | |
9078 | ||
9079 | Press @code{RET} on such a submodule section to show its own status buffer. | |
9080 | Press @code{RET} on the "Modules" section to display a list of submodules | |
9081 | in a separate buffer. This shows additional information not | |
9082 | displayed in the super-repository's status buffer. | |
9083 | @end defun | |
9084 | 9165 | |
9085 | 9166 | @node Submodule Transient |
9086 | 9167 | @subsection Submodule Transient |
9325 | 9406 | beside the ones below, but these didn't fit well anywhere else. |
9326 | 9407 | |
9327 | 9408 | @table @asis |
9328 | @kindex M-w | |
9409 | @kindex C-w | |
9329 | 9410 | @cindex magit-copy-section-value |
9330 | @item @kbd{M-w} @tie{}@tie{}@tie{}@tie{}(@code{magit-copy-section-value}) | |
9411 | @item @kbd{C-w} @tie{}@tie{}@tie{}@tie{}(@code{magit-copy-section-value}) | |
9331 | 9412 | |
9332 | 9413 | This command saves the value of the current section to the |
9333 | 9414 | @code{kill-ring}, and, provided that the current section is a commit, |
9340 | 9421 | |
9341 | 9422 | When the region is active, this command saves that to the |
9342 | 9423 | @code{kill-ring}, like @code{kill-ring-save} would, instead of behaving as |
9343 | described above. If a prefix argument is used and the region is | |
9344 | within a hunk, it strips the outer diff marker column before saving | |
9345 | the text. | |
9346 | ||
9347 | @kindex C-w | |
9424 | described above. If a prefix argument is used and the region is | |
9425 | within a hunk, then it strips the diff marker column and keeps | |
9426 | only either the added or removed lines, depending on the sign of | |
9427 | the prefix argument. | |
9428 | ||
9429 | @kindex M-w | |
9348 | 9430 | @cindex magit-copy-buffer-revision |
9349 | @item @kbd{C-w} @tie{}@tie{}@tie{}@tie{}(@code{magit-copy-buffer-revision}) | |
9431 | @item @kbd{M-w} @tie{}@tie{}@tie{}@tie{}(@code{magit-copy-buffer-revision}) | |
9350 | 9432 | |
9351 | 9433 | This command saves the revision being displayed in the current buffer |
9352 | 9434 | to the @code{kill-ring} and also pushes it to the @code{magit-revision-stack}. It |
9630 | 9712 | Mode-line lighter for @code{magit-wip-initial-backup-mode}. |
9631 | 9713 | @end defopt |
9632 | 9714 | |
9633 | @node Minor Mode for Buffers Visiting Files | |
9634 | @section Minor Mode for Buffers Visiting Files | |
9635 | ||
9636 | The minor-mode @code{magit-file-mode} enables certain Magit features in | |
9637 | file-visiting buffers belonging to a Git repository. The globalized | |
9638 | variant @code{global-magit-file-mode} enables the local mode in all such | |
9639 | buffers. It is enabled by default. Currently the local mode only | |
9640 | establishes a few key bindings, but this might be extended in the | |
9641 | future. | |
9642 | ||
9643 | @defopt global-magit-file-mode | |
9644 | ||
9645 | Whether to establish certain Magit key bindings in all file-visiting | |
9646 | buffers belonging to any Git repository. This is enabled by default. | |
9647 | This globalized mode turns on the local minor-mode @code{magit-file-mode} | |
9648 | in all suitable buffers. | |
9649 | @end defopt | |
9650 | ||
9651 | @defvar magit-file-mode-map | |
9652 | ||
9653 | This keymap is used by the local minor-mode @code{magit-file-mode} and | |
9654 | establishes the key bindings described below. | |
9655 | ||
9656 | Note that the default binding for @code{magit-file-dispatch} is very | |
9657 | cumbersome to use and that we recommend that you add a better | |
9658 | binding. | |
9659 | ||
9660 | Instead of @code{C-c M-g} I would have preferred to use @code{C-c g} because (1) | |
9661 | it is similar to @code{C-x g} (the recommended global binding for | |
9662 | @code{~magit-status}), (2) we cannot use @code{C-c C-g} because we have been | |
9663 | recommending that that be bound to @code{magit-dispatch} for a long time, | |
9664 | (3) we cannot use @code{C-x C-g} because that is a convenient way of | |
9665 | aborting the incomplete key sequence @code{C-x}, and most importantly (4) | |
9666 | it would make it much easier to type the next key (a suffix binding) | |
9667 | because most of those are letters. | |
9668 | ||
9669 | For example @code{C-c g b} is much easier to type than @code{C-c M-g b}. For | |
9670 | suffix bindings that use uppercase letters, the default is just | |
9671 | horrible—having to use e.g. @code{C-c M-g B} (@code{Control+c Meta+g Shift+b}) | |
9672 | would drive anyone up the walls (or to Vim). | |
9673 | ||
9674 | However @code{C-c LETTER} bindings are reserved for users (see | |
9675 | @ref{Key Binding Conventions,,,elisp,}). Packages are forbidden from | |
9676 | using those. Doing so anyway is considered heresy. Therefore if | |
9677 | you want a better binding, you have to add it yourself: | |
9715 | @node Commands for Buffers Visiting Files | |
9716 | @section Commands for Buffers Visiting Files | |
9717 | ||
9718 | Magit defines a few global key bindings unless the user sets | |
9719 | @code{magit-define-global-key-bindings} to @code{nil}. This includes binding @code{C-c | |
9720 | M-g} to @code{magit-file-dispatch}. @code{C-c g} would be a much better binding | |
9721 | but the @code{C-c <letter>} namespace is reserved for users, meaning that | |
9722 | packages are not allowed to use it. If you want to use @code{C-c g}, then | |
9723 | you have to add that binding yourself. Also see @ref{Default Bindings} | |
9724 | and @ref{Key Binding Conventions,,,elisp,}. | |
9725 | ||
9726 | If you want a better binding, you have to add it yourself: | |
9678 | 9727 | |
9679 | 9728 | @lisp |
9680 | (define-key magit-file-mode-map | |
9681 | (kbd "C-c g") 'magit-file-dispatch) | |
9729 | (global-set-key (kbd "C-c g") 'magit-file-dispatch) | |
9682 | 9730 | @end lisp |
9683 | @end defvar | |
9684 | 9731 | |
9685 | 9732 | The key bindings shown below assume that you have not improved the |
9686 | 9733 | binding for @code{magit-file-dispatch}. |
9692 | 9739 | |
9693 | 9740 | This transient prefix command binds the following suffix commands |
9694 | 9741 | and displays them in a temporary buffer until a suffix is invoked. |
9742 | ||
9743 | When invoked in a buffer that does not visit a file, then it falls | |
9744 | back to regular @code{magit-dispatch}. | |
9695 | 9745 | |
9696 | 9746 | @kindex C-c M-g s |
9697 | 9747 | @cindex magit-stage-file |
10022 | 10072 | @menu |
10023 | 10073 | * Safety:: |
10024 | 10074 | * Performance:: |
10075 | * Default Bindings:: | |
10025 | 10076 | @end menu |
10026 | 10077 | |
10027 | 10078 | @node Safety |
10212 | 10263 | the diff, which is useful because it increases the odds that you spot |
10213 | 10264 | potential issues. |
10214 | 10265 | |
10215 | @unnumberedsubsubsec The Built-In VC Package | |
10216 | ||
10217 | Emacs comes with a version control interface called "VC", see | |
10218 | @ref{Version Control,,,emacs,}. It is enabled be default, and if you don't | |
10219 | use it in addition to Magit, then you should disable it to keep it | |
10220 | from performing unnecessary work: | |
10221 | ||
10222 | @lisp | |
10223 | (setq vc-handled-backends nil) | |
10224 | @end lisp | |
10225 | ||
10226 | You can also disable its use for Git but keep using it when using | |
10227 | another version control system: | |
10228 | ||
10229 | @lisp | |
10230 | (setq vc-handled-backends (delq 'Git vc-handled-backends)) | |
10231 | @end lisp | |
10232 | ||
10233 | 10266 | @node Microsoft Windows Performance |
10234 | 10267 | @unnumberedsubsubsec Microsoft Windows Performance |
10235 | 10268 | |
10274 | 10307 | |
10275 | 10308 | On Catalina, and potentially other macOS releases, there may be a |
10276 | 10309 | performance problem where any action takes 20 times longer on Darwin |
10277 | than on Linux. This can be fixed by setting @code{magit-git-executable} to | |
10278 | the absolute path of the @code{git} executable, instead of relying on | |
10279 | resolving the @code{$PATH}. | |
10310 | than on Linux. This can be worked around by setting | |
10311 | @code{magit-git-executable} to the absolute path of the @code{git} executable, | |
10312 | instead of relying on resolving the @code{$PATH}. You should not do that if | |
10313 | you want to use Magit on remote machines using Tramp and if @code{git} is not | |
10314 | installed in the same location on those machines. | |
10315 | ||
10316 | @node Default Bindings | |
10317 | @subsection Default Bindings | |
10318 | ||
10319 | @defopt magit-define-global-key-bindings | |
10320 | ||
10321 | This option controls whether some Magit commands are automatically | |
10322 | bound in the global keymap even before Magit is used for the first | |
10323 | time in the current session. | |
10324 | ||
10325 | If this variable is non-nil, which it is by default, then the | |
10326 | following bindings may be added to the global keymap. | |
10327 | ||
10328 | @multitable {aaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaa} | |
10329 | @item @code{C-x g} | |
10330 | @tab @code{magit-status} | |
10331 | @item @code{C-x M-g} | |
10332 | @tab @code{magit-dispatch} | |
10333 | @item @code{C-c M-g} | |
10334 | @tab @code{magit-file-dispatch} | |
10335 | @end multitable | |
10336 | ||
10337 | These bindings may be added when @code{after-init-hook} is called. | |
10338 | Each binding is added if and only if at that time no other key | |
10339 | is bound to the same command and no other command is bound to | |
10340 | the same key. In other words we try to avoid adding bindings | |
10341 | that are unnecessary, as well as bindings that conflict with | |
10342 | other bindings. | |
10343 | ||
10344 | Adding the above bindings is delayed until @code{after-init-hook} | |
10345 | is called to allow users to set the variable anywhere in their | |
10346 | init file (without having to make sure to do so before @code{magit} | |
10347 | is loaded or autoloaded) and to increase the likelihood that | |
10348 | all the potentially conflicting user bindings have already | |
10349 | been added. | |
10350 | ||
10351 | Setting this variable after the hook has already been called | |
10352 | has no effect. | |
10353 | ||
10354 | We recommend that you bind @code{C-c g} instead of @code{C-c M-g} to | |
10355 | @code{magit-file-dispatch}. The former is a much better binding | |
10356 | but the @code{C-c <letter>} namespace is strictly reserved for | |
10357 | users; preventing Magit from using it by default. | |
10358 | ||
10359 | @lisp | |
10360 | (global-set-key (kbd "C-c g") 'magit-file-dispatch) | |
10361 | @end lisp | |
10362 | ||
10363 | Also see @ref{Commands for Buffers Visiting Files} and @ref{Key Binding Conventions,,,elisp,}. | |
10364 | @end defopt | |
10280 | 10365 | |
10281 | 10366 | @node Plumbing |
10282 | 10367 | @chapter Plumbing |
10461 | 10546 | Calls git synchronously with ARGS and then refreshes. |
10462 | 10547 | @end defun |
10463 | 10548 | |
10464 | @defun magit-run-git-with-input input &rest args | |
10465 | ||
10466 | Calls git synchronously with ARGS and sends it INPUT on standard | |
10467 | input. | |
10468 | ||
10469 | INPUT should be a buffer or the name of an existing buffer. The | |
10470 | content of that buffer is used as the process' standard input. | |
10471 | After the process returns a refresh is performed. | |
10472 | ||
10473 | As a special case, INPUT may also be nil. In that case the content | |
10474 | of the current buffer is used as standard input and @strong{no} refresh is | |
10475 | performed. | |
10476 | ||
10477 | This function actually runs git asynchronously. But then it waits | |
10478 | for the process to return, so the function itself is synchronous. | |
10549 | @defun magit-run-git-with-input &rest args | |
10550 | ||
10551 | Calls git synchronously with ARGS and sends it the content of the | |
10552 | current buffer on standard input. | |
10553 | ||
10554 | If the current buffer's @code{default-directory} is on a remote | |
10555 | filesystem, this function actually runs git asynchronously. But | |
10556 | then it waits for the process to return, so the function itself is | |
10557 | synchronous. | |
10479 | 10558 | @end defun |
10480 | 10559 | |
10481 | 10560 | @defun magit-run-git-with-logfile file &rest args |
10482 | 10561 | |
10483 | Calls git synchronously with ARGS@. The process' output is saved in | |
10562 | Calls git synchronously with ARGS@. The process's output is saved in | |
10484 | 10563 | FILE@. This is rarely useful and so this function might be removed |
10485 | 10564 | in the future. |
10486 | 10565 | |
10532 | 10611 | still alive), as well as the respective Magit status buffer. |
10533 | 10612 | @end defun |
10534 | 10613 | |
10535 | @defun magit-start-git &rest args | |
10614 | @defun magit-start-git input &rest args | |
10536 | 10615 | |
10537 | 10616 | Start Git, prepare for refresh, and return the process object. |
10538 | 10617 | |
11114 | 11193 | @appendixsec FAQ - How to @dots{}? |
11115 | 11194 | |
11116 | 11195 | @menu |
11196 | * How to pronounce Magit?:: | |
11117 | 11197 | * How to show git's output?:: |
11118 | 11198 | * How to install the gitman info manual?:: |
11119 | 11199 | * How to show diffs for gpg-encrypted files?:: |
11120 | 11200 | * How does branching and pushing work?:: |
11121 | 11201 | * Can Magit be used as @code{ediff-version-control-package}?:: |
11202 | * Should I disable VC@?:: | |
11122 | 11203 | @end menu |
11204 | ||
11205 | @node How to pronounce Magit? | |
11206 | @appendixsubsec How to pronounce Magit? | |
11207 | ||
11208 | Either @code{mu[m's] git} or @code{magi@{c => t@}} is fine. | |
11209 | ||
11210 | The slogan is "It's Magit! The magical Git client", so it makes sense | |
11211 | to pronounce Magit like magic, while taking into account that C and T | |
11212 | do not sound the same. | |
11213 | ||
11214 | The German "Magie" is not pronounced the same as the English "magic", | |
11215 | so if you speak German then you can use the above rational to justify | |
11216 | using the former pronunciation; @code{Mag@{ie => it@}}. | |
11217 | ||
11218 | You can also choose to use the former pronunciation just because you | |
11219 | like it better. | |
11220 | ||
11221 | Also see @uref{https://magit.vc/assets/videos/magic.mp4}. | |
11222 | Also see @uref{https://emacs.stackexchange.com/questions/13696}. | |
11123 | 11223 | |
11124 | 11224 | @node How to show git's output? |
11125 | 11225 | @appendixsubsec How to show git's output? |
11205 | 11305 | command @code{magit-ediff-resolve} which only shows yet-to-be resolved |
11206 | 11306 | conflicts. |
11207 | 11307 | |
11308 | @node Should I disable VC@? | |
11309 | @appendixsubsec Should I disable VC@? | |
11310 | ||
11311 | If you don't use VC (the built-in version control interface) then | |
11312 | you might be tempted to disable it, not least because we used to | |
11313 | recommend that you do that. | |
11314 | ||
11315 | We no longer recommend that you disable VC@. Doing so would break | |
11316 | useful third-party packages (such as @code{diff-hl}), which depend on VC | |
11317 | being enabled. | |
11318 | ||
11319 | If you choose to disable VC anyway, then you can do so by changing | |
11320 | the value of @code{vc-handled-backends}. | |
11321 | ||
11208 | 11322 | @node FAQ - Issues and Errors |
11209 | 11323 | @appendixsec FAQ - Issues and Errors |
11210 | 11324 | |
11314 | 11428 | But doing so isn't good for performance. For more (overly optimistic) |
11315 | 11429 | information see @ref{VC Mode Line,,,emacs,}. |
11316 | 11430 | |
11317 | If you don't really care about seeing that information in the | |
11318 | mode-line, but just don't want to see @emph{incorrect} information, then | |
11319 | consider disabling VC when using Git: | |
11431 | If you don't really care about seeing this information in the | |
11432 | mode-line, but just don't want to see @emph{incorrect} information, | |
11433 | then consider simply not displaying it in the mode-line: | |
11320 | 11434 | |
11321 | 11435 | @lisp |
11322 | (setq vc-handled-backends (delq 'Git vc-handled-backends)) | |
11323 | @end lisp | |
11324 | ||
11325 | Or to disable it completely: | |
11326 | ||
11327 | @lisp | |
11328 | (setq vc-handled-backends nil) | |
11436 | (setq-default mode-line-format | |
11437 | (delete '(vc-mode vc-mode) mode-line-format)) | |
11329 | 11438 | @end lisp |
11330 | 11439 | |
11331 | 11440 | @node A branch and tag sharing the same name breaks SOMETHING |
11401 | 11510 | @end example |
11402 | 11511 | |
11403 | 11512 | This will actually end up using @code{emacs}, not @code{emacsclient}. If you do |
11404 | this, then can still edit the commit message but @code{git-commit-mode} won't | |
11405 | be used and you have to exit @code{emacs} to finish the process. | |
11513 | this, then you can still edit the commit message but @code{git-commit-mode} | |
11514 | won't be used and you have to exit @code{emacs} to finish the process. | |
11406 | 11515 | |
11407 | 11516 | Tautology ahead. If you want to be able to use @code{emacsclient} to connect |
11408 | 11517 | to a running @code{emacs} instance, even though no @code{emacs} instance is running, |
0 | ||
1 | GNU GENERAL PUBLIC LICENSE | |
2 | Version 3, 29 June 2007 | |
3 | ||
4 | Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | |
0 | GNU GENERAL PUBLIC LICENSE | |
1 | Version 3, 29 June 2007 | |
2 | ||
3 | Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> | |
5 | 4 | Everyone is permitted to copy and distribute verbatim copies |
6 | 5 | of this license document, but changing it is not allowed. |
7 | 6 | |
8 | Preamble | |
7 | Preamble | |
9 | 8 | |
10 | 9 | The GNU General Public License is a free, copyleft license for |
11 | 10 | software and other kinds of works. |
68 | 67 | The precise terms and conditions for copying, distribution and |
69 | 68 | modification follow. |
70 | 69 | |
71 | TERMS AND CONDITIONS | |
70 | TERMS AND CONDITIONS | |
72 | 71 | |
73 | 72 | 0. Definitions. |
74 | 73 | |
76 | 75 | |
77 | 76 | "Copyright" also means copyright-like laws that apply to other kinds of |
78 | 77 | works, such as semiconductor masks. |
79 | ||
78 | ||
80 | 79 | "The Program" refers to any copyrightable work licensed under this |
81 | 80 | License. Each licensee is addressed as "you". "Licensees" and |
82 | 81 | "recipients" may be individuals or organizations. |
509 | 508 | covered work in a country, or your recipient's use of the covered work |
510 | 509 | in a country, would infringe one or more identifiable patents in that |
511 | 510 | country that you have reason to believe are valid. |
512 | ||
511 | ||
513 | 512 | If, pursuant to or in connection with a single transaction or |
514 | 513 | arrangement, you convey, or propagate by procuring conveyance of, a |
515 | 514 | covered work, and grant a patent license to some of the parties |
618 | 617 | Program, unless a warranty or assumption of liability accompanies a |
619 | 618 | copy of the Program in return for a fee. |
620 | 619 | |
621 | END OF TERMS AND CONDITIONS | |
622 | ||
623 | How to Apply These Terms to Your New Programs | |
620 | END OF TERMS AND CONDITIONS | |
621 | ||
622 | How to Apply These Terms to Your New Programs | |
624 | 623 | |
625 | 624 | If you develop a new program, and you want it to be of the greatest |
626 | 625 | possible use to the public, the best way to achieve this is to make it |
645 | 644 | GNU General Public License for more details. |
646 | 645 | |
647 | 646 | You should have received a copy of the GNU General Public License |
648 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
647 | along with this program. If not, see <https://www.gnu.org/licenses/>. | |
649 | 648 | |
650 | 649 | Also add information on how to contact you by electronic and paper mail. |
651 | 650 | |
664 | 663 | You should also get your employer (if you work as a programmer) or school, |
665 | 664 | if any, to sign a "copyright disclaimer" for the program, if necessary. |
666 | 665 | For more information on this, and how to apply and follow the GNU GPL, see |
667 | <http://www.gnu.org/licenses/>. | |
666 | <https://www.gnu.org/licenses/>. | |
668 | 667 | |
669 | 668 | The GNU General Public License does not permit incorporating your program |
670 | 669 | into proprietary programs. If your program is a subroutine library, you |
671 | 670 | may consider it more useful to permit linking proprietary applications with |
672 | 671 | the library. If this is what you want to do, use the GNU Lesser General |
673 | 672 | Public License instead of this License. But first, please read |
674 | <http://www.gnu.org/philosophy/why-not-lgpl.html>. | |
675 | ||
673 | <https://www.gnu.org/licenses/why-not-lgpl.html>. |
49 | 49 | $(info ====) |
50 | 50 | $(info ) |
51 | 51 | $(info make test - run tests) |
52 | $(info make test-git - run tests using Git functions) | |
53 | $(info make test-libgit - run tests using libgit functions) | |
52 | 54 | $(info make test-interactive - run tests interactively) |
53 | 55 | $(info make emacs-Q - run emacs -Q plus Magit) |
54 | 56 | $(info ) |
110 | 112 | (load-file \"t/magit-tests.el\")\ |
111 | 113 | (ert-run-tests-batch-and-exit))" |
112 | 114 | |
115 | test-git: | |
116 | @$(BATCH) --eval "(progn\ | |
117 | $$suppress_warnings\ | |
118 | (require 'magit)\ | |
119 | (setq magit-inhibit-libgit t)\ | |
120 | (unless (eq 'git (magit-gitimpl))\ | |
121 | (message \"Git implementation not being used.\")\ | |
122 | (kill-emacs 1))\ | |
123 | (load-file \"t/magit-tests.el\")\ | |
124 | (ert-run-tests-batch-and-exit))" | |
125 | ||
126 | test-libgit: | |
127 | @$(BATCH) --eval "(progn\ | |
128 | $$suppress_warnings\ | |
129 | (require 'magit)\ | |
130 | (unless (eq 'libgit (magit-gitimpl))\ | |
131 | (message \"libgit not available.\")\ | |
132 | (kill-emacs 1))\ | |
133 | (load-file \"t/magit-tests.el\")\ | |
134 | (ert-run-tests-batch-and-exit))" | |
135 | ||
113 | 136 | test-interactive: |
114 | 137 | @$(EMACSBIN) -Q $(LOAD_PATH) --eval "(progn\ |
115 | 138 | (load-file \"t/magit-tests.el\")\ |
196 | 219 | `((emacs ,emacs-version) ;` |
197 | 220 | (dash ,dash-version) |
198 | 221 | (transient ,transient-version) |
199 | (with-editor ,with-editor-version))))) | |
222 | (with-editor ,with-editor-version)))) | |
223 | (re-search-forward "^;; Package-Version: ") | |
224 | (delete-region (point) (line-end-position)) | |
225 | (insert git-commit-version)) | |
226 | ||
200 | 227 | (with-temp-file "lisp/magit-libgit.el" |
201 | 228 | (insert-file-contents "lisp/magit-libgit.el") |
202 | 229 | (re-search-forward "^;; Package-Requires: ") |
204 | 231 | (insert (format "%S" |
205 | 232 | `((emacs "$(LIBGIT_EMACS_VERSION)") ;` |
206 | 233 | (magit "$(LIBGIT_MAGIT_VERSION)") |
207 | (libgit ,libgit-version))))) | |
234 | (libgit ,libgit-version)))) | |
235 | (re-search-forward "^;; Package-Version: ") | |
236 | (delete-region (point) (line-end-position)) | |
237 | (insert magit-libgit-version)) | |
208 | 238 | (with-temp-file "lisp/magit-section.el" |
209 | 239 | (insert-file-contents "lisp/magit-section.el") |
210 | 240 | (re-search-forward "^;; Package-Requires: ") |
211 | 241 | (delete-region (point) (line-end-position)) |
212 | 242 | (insert (format "%S" |
213 | 243 | `((emacs ,emacs-version) ;` |
214 | (dash ,dash-version))))) | |
244 | (dash ,dash-version)))) | |
245 | (re-search-forward "^;; Package-Version: ") | |
246 | (delete-region (point) (line-end-position)) | |
247 | (insert magit-section-version)) | |
215 | 248 | (with-temp-file "lisp/magit-pkg.el" |
216 | (insert (pp-to-string | |
217 | `(define-package "magit" "$(VERSION)" ;` | |
218 | "A Git porcelain inside Emacs." | |
219 | '((emacs ,emacs-version) ;' | |
220 | (async ,async-version) | |
221 | (dash ,dash-version) | |
222 | (git-commit ,git-commit-version) | |
223 | ;; FIXME (magit-section ,magit-section-version) | |
224 | (transient ,transient-version) | |
225 | (with-editor ,with-editor-version)) | |
226 | :keywords '("git" "tools" "vc")))) ;' | |
249 | (insert (format | |
250 | "(define-package \"magit\" \"$(VERSION)\"\ | |
251 | \"A Git porcelain inside Emacs.\" | |
252 | '((emacs %S) | |
253 | (dash %S) | |
254 | (git-commit %S) | |
255 | (magit-section %S) | |
256 | (transient %S) | |
257 | (with-editor %S)) | |
258 | :homepage \"https://magit.vc\" | |
259 | :keywords '(\"git\" \"tools\" \"vc\")) | |
260 | " emacs-version | |
261 | dash-version | |
262 | git-commit-version | |
263 | magit-section-version | |
264 | transient-version | |
265 | with-editor-version)) | |
227 | 266 | (goto-char (point-min)) |
228 | 267 | (re-search-forward " \"A") |
229 | 268 | (goto-char (match-beginning 0)) |
235 | 274 | bump-versions-1: |
236 | 275 | @$(BATCH) --eval "(let (\ |
237 | 276 | (emacs-version \"$(EMACS_VERSION)\")\ |
238 | (async-version \"$(ASYNC_VERSION)\")\ | |
239 | 277 | (dash-version \"$(DASH_VERSION)\")\ |
240 | 278 | (git-commit-version \"$(GIT_COMMIT_VERSION)\")\ |
241 | 279 | (libgit-version \"$(LIBGIT_VERSION)\")\ |
280 | (magit-libgit-version \"$(MAGIT_LIBGIT_VERSION)\")\ | |
242 | 281 | (magit-section-version \"$(MAGIT_SECTION_VERSION)\")\ |
243 | 282 | (transient-version \"$(TRANSIENT_VERSION)\")\ |
244 | 283 | (with-editor-version \"$(WITH_EDITOR_VERSION)\"))\ |
247 | 286 | bump-snapshots: |
248 | 287 | @$(BATCH) --eval "(let (\ |
249 | 288 | (emacs-version \"$(EMACS_VERSION)\")\ |
250 | (async-version \"$(ASYNC_MELPA_SNAPSHOT)\")\ | |
251 | 289 | (dash-version \"$(DASH_MELPA_SNAPSHOT)\")\ |
252 | 290 | (git-commit-version \"$(GIT_COMMIT_MELPA_SNAPSHOT)\")\ |
253 | 291 | (libgit-version \"$(LIBGIT_MELPA_SNAPSHOT)\")\ |
292 | (magit-libgit-version \"$(MAGIT_LIBGIT_MELPA_SNAPSHOT)\")\ | |
254 | 293 | (magit-section-version \"$(MAGIT_SECTION_MELPA_SNAPSHOT)\")\ |
255 | 294 | (transient-version \"$(TRANSIENT_MELPA_SNAPSHOT)\")\ |
256 | 295 | (with-editor-version \"$(WITH_EDITOR_MELPA_SNAPSHOT)\"))\ |
130 | 130 | *** |
131 | 131 | [![Paren Xkcb](https://img.shields.io/badge/%28-%20%20%20-red.svg)](https://xkcd.com/859) |
132 | 132 | [![GPL v3](https://img.shields.io/badge/license-GPL_v3-green.svg)](http://www.gnu.org/licenses/gpl-3.0.txt) |
133 | [![Build Status](https://travis-ci.org/magit/magit.svg?branch=master)](https://travis-ci.org/magit/magit) | |
133 | [![Build Status](https://github.com/magit/magit/workflows/test/badge.svg?branch=master)](https://github.com/magit/magit/actions) | |
134 | 134 | [![Melpa](https://melpa.org/packages/magit-badge.svg)](https://melpa.org/#/magit) |
135 | 135 | [![Melpa Stable](https://stable.melpa.org/packages/magit-badge.svg)](https://stable.melpa.org/#/magit) |
136 | [![Git Xkcd](https://img.shields.io/badge/xkcd-git-orange.svg)](https://xkcd.com/1597) | |
137 | 136 | [![Eierlegende Wollmilchsau](https://img.shields.io/badge/eierlegende-Wollmilchsau-green.svg)](https://magit.vc/manual/magit) |
138 | 137 | [![Swiss Made](https://img.shields.io/badge/swiss-made-red.svg?colorA=E11A27&colorB=555555)](https://magit.vc/stats/authors.html#commits_per_author) |
139 | 138 | [![Netscape](https://magit.vc/assets/netscape-20px.png)](https://en.wikipedia.org/wiki/Browser_wars) |
53 | 53 | ifeq "$(BUILD_MAGIT_LIBGIT)" "true" |
54 | 54 | ELS += magit-libgit.el |
55 | 55 | endif |
56 | ELS += magit-git.el | |
56 | 57 | ELS += magit-mode.el |
57 | 58 | ELS += magit-margin.el |
58 | 59 | ELS += magit-process.el |
100 | 101 | |
101 | 102 | ## Versions ########################################################## |
102 | 103 | |
103 | VERSION ?= $(shell test -e $(TOP).git && git describe --tags --abbrev=0 | cut -c2-) | |
104 | ||
105 | ASYNC_VERSION = 1.9.3 | |
106 | DASH_VERSION = 2.14.1 | |
107 | GIT_COMMIT_VERSION = 3.0.0 | |
108 | LIBGIT_VERSION = 0 | |
109 | MAGIT_SECTION_VERSION = 3.0.0 | |
110 | TRANSIENT_VERSION = 0 | |
111 | WITH_EDITOR_VERSION = 2.8.0 | |
112 | ||
113 | ASYNC_MELPA_SNAPSHOT = 20180527 | |
114 | DASH_MELPA_SNAPSHOT = 20180910 | |
115 | GIT_COMMIT_MELPA_SNAPSHOT = 20181104 | |
116 | LIBGIT_MELPA_SNAPSHOT = 0 | |
117 | MAGIT_SECTION_MELPA_SNAPSHOT = 20200123 | |
118 | TRANSIENT_MELPA_SNAPSHOT = 20190812 | |
119 | WITH_EDITOR_MELPA_SNAPSHOT = 20181103 | |
104 | VERSION ?= $(shell \ | |
105 | test -e $(TOP).git && \ | |
106 | git describe --tags --abbrev=0 --always | cut -c2-) | |
107 | ||
108 | DASH_VERSION = 2.18.1 | |
109 | GIT_COMMIT_VERSION = $(VERSION) | |
110 | LIBGIT_VERSION = 0 | |
111 | MAGIT_LIBGIT_VERSION = 0 | |
112 | MAGIT_SECTION_VERSION = $(VERSION) | |
113 | TRANSIENT_VERSION = 0.3.3 | |
114 | WITH_EDITOR_VERSION = 3.0.4 | |
115 | ||
116 | DASH_MELPA_SNAPSHOT = 20210330 | |
117 | GIT_COMMIT_MELPA_SNAPSHOT = 20210524 | |
118 | LIBGIT_MELPA_SNAPSHOT = 0 | |
119 | MAGIT_LIBGIT_MELPA_SNAPSHOT = 0 | |
120 | MAGIT_SECTION_MELPA_SNAPSHOT = 20210524 | |
121 | TRANSIENT_MELPA_SNAPSHOT = 20210524 | |
122 | WITH_EDITOR_MELPA_SNAPSHOT = 20210524 | |
120 | 123 | |
121 | 124 | EMACS_VERSION = 25.1 |
122 | 125 | |
123 | 126 | LIBGIT_EMACS_VERSION = 26.1 |
124 | LIBGIT_MAGIT_VERSION = 0 | |
127 | LIBGIT_MAGIT_VERSION = $(VERSION) | |
125 | 128 | |
126 | 129 | EMACSOLD := $(shell $(BATCH) --eval \ |
127 | 130 | "(and (version< emacs-version \"$(EMACS_VERSION)\") (princ \"true\"))") |
133 | 136 | |
134 | 137 | ifndef LOAD_PATH |
135 | 138 | |
136 | ELPA_DIR ?= $(HOME)/.emacs.d/elpa | |
139 | USER_EMACS_DIR = $(HOME)/.emacs.d | |
140 | ifeq "$(wildcard $(USER_EMACS_DIR))" "" | |
141 | XDG_CONFIG_DIR = $(or $(XDG_CONFIG_HOME),$(HOME)/.config) | |
142 | ifneq "$(wildcard $(XDG_CONFIG_DIR)/emacs)" "" | |
143 | USER_EMACS_DIR = $(XDG_CONFIG_DIR)/emacs | |
144 | endif | |
145 | endif | |
146 | ||
147 | ELPA_DIR ?= $(USER_EMACS_DIR)/elpa | |
137 | 148 | |
138 | 149 | DASH_DIR ?= $(shell \ |
139 | 150 | find -L $(ELPA_DIR) -maxdepth 1 -regex '.*/dash-[.0-9]*' 2> /dev/null | \ |
171 | 182 | LOAD_PATH = -L $(TOP)lisp |
172 | 183 | |
173 | 184 | # When making changes here, then don't forget to adjust "Makefile", |
174 | # ".travis.yml", ".github/ISSUE_TEMPLATE/bug_report.md", | |
185 | # ".github/workflows/test.yml", ".github/ISSUE_TEMPLATE/bug_report.md", | |
175 | 186 | # `magit-emacs-Q-command' and the "Installing from the Git Repository" |
176 | 187 | # info node accordingly. Also don't forget to "rgrep \b<pkg>\b". |
177 | 188 | |
192 | 203 | ifndef ORG_LOAD_PATH |
193 | 204 | ORG_LOAD_PATH = $(LOAD_PATH) |
194 | 205 | ORG_LOAD_PATH += -L ../../org/lisp |
195 | ORG_LOAD_PATH += -L ../../org/contrib/lisp | |
206 | ORG_LOAD_PATH += -L ../../org-contrib/lisp | |
196 | 207 | ORG_LOAD_PATH += -L ../../ox-texinfo+ |
197 | 208 | endif |
198 | 209 |
0 | 0 | ;;; git-commit.el --- Edit Git commit messages -*- lexical-binding: t; -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | ;; Authors: Jonas Bernoulli <jonas@bernoul.li> | |
8 | ;; Sebastian Wiesner <lunaryorn@gmail.com> | |
9 | ;; Florian Ragwitz <rafl@debian.org> | |
10 | ;; Marius Vollmer <marius.vollmer@gmail.com> | |
7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> | |
8 | ;; Sebastian Wiesner <lunaryorn@gmail.com> | |
9 | ;; Florian Ragwitz <rafl@debian.org> | |
10 | ;; Marius Vollmer <marius.vollmer@gmail.com> | |
11 | 11 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
12 | 12 | |
13 | ;; Package-Requires: ((emacs "25.1") (dash "20180910") (transient "20190812") (with-editor "20181103")) | |
14 | 13 | ;; Keywords: git tools vc |
15 | 14 | ;; Homepage: https://github.com/magit/magit |
16 | ||
17 | ;; This file is not part of GNU Emacs. | |
15 | ;; Package-Requires: ((emacs "25.1") (dash "2.18.1") (transient "0.3.3") (with-editor "3.0.4")) | |
16 | ;; Package-Version: 3.0.0 | |
17 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
18 | 18 | |
19 | 19 | ;; This file is free software; you can redistribute it and/or modify |
20 | 20 | ;; it under the terms of the GNU General Public License as published by |
21 | 21 | ;; the Free Software Foundation; either version 3, or (at your option) |
22 | 22 | ;; any later version. |
23 | ||
23 | ;; | |
24 | 24 | ;; This file is distributed in the hope that it will be useful, |
25 | 25 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 | 26 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
27 | 27 | ;; GNU General Public License for more details. |
28 | ||
28 | ;; | |
29 | 29 | ;; You should have received a copy of the GNU General Public License |
30 | 30 | ;; along with this file. If not, see <http://www.gnu.org/licenses/>. |
31 | 31 | |
112 | 112 | ;;;; Dependencies |
113 | 113 | |
114 | 114 | (require 'dash) |
115 | (require 'log-edit) | |
115 | (require 'subr-x) | |
116 | ||
116 | 117 | (require 'magit-git nil t) |
117 | 118 | (require 'magit-utils nil t) |
119 | ||
120 | (require 'log-edit) | |
118 | 121 | (require 'ring) |
122 | (require 'rx) | |
119 | 123 | (require 'server) |
120 | 124 | (require 'transient) |
121 | 125 | (require 'with-editor) |
122 | 126 | |
123 | (eval-when-compile | |
124 | (require 'recentf) | |
125 | (require 'subr-x)) | |
127 | (defvar recentf-exclude) | |
126 | 128 | |
127 | 129 | ;;;; Declarations |
128 | 130 | |
149 | 151 | :link '(info-link "(magit)Editing Commit Messages") |
150 | 152 | :group 'tools) |
151 | 153 | |
152 | ;;;###autoload | |
153 | 154 | (define-minor-mode global-git-commit-mode |
154 | 155 | "Edit Git commit messages. |
156 | ||
155 | 157 | This global mode arranges for `git-commit-setup' to be called |
156 | 158 | when a Git commit message file is opened. That usually happens |
157 | 159 | when Git uses the Emacsclient as $GIT_EDITOR to have the user |
158 | provide such a commit message." | |
160 | provide such a commit message. | |
161 | ||
162 | Loading the library `git-commit' by default enables this mode, | |
163 | but the library is not automatically loaded because doing that | |
164 | would pull in many dependencies and increase startup time too | |
165 | much. You can either rely on `magit' loading this library or | |
166 | you can load it explicitly. Autoloading is not an alternative | |
167 | because in this case autoloading would immediately trigger | |
168 | full loading." | |
159 | 169 | :group 'git-commit |
160 | 170 | :type 'boolean |
161 | 171 | :global t |
174 | 184 | `git-commit-mode'." |
175 | 185 | :group 'git-commit |
176 | 186 | :type '(choice (function-item text-mode) |
187 | (function-item markdown-mode) | |
188 | (function-item org-mode) | |
189 | (function-item fundamental-mode) | |
190 | (function-item git-commit-elisp-text-mode) | |
191 | (function :tag "Another mode") | |
177 | 192 | (const :tag "No major mode"))) |
193 | ;;;###autoload(put 'git-commit-major-mode 'safe-local-variable | |
194 | ;;;###autoload (lambda (val) | |
195 | ;;;###autoload (memq val '(text-mode | |
196 | ;;;###autoload markdown-mode | |
197 | ;;;###autoload org-mode | |
198 | ;;;###autoload fundamental-mode | |
199 | ;;;###autoload git-commit-elisp-text-mode)))) | |
178 | 200 | |
179 | 201 | (defcustom git-commit-setup-hook |
180 | 202 | '(git-commit-save-message |
189 | 211 | :get (and (featurep 'magit-utils) 'magit-hook-custom-get) |
190 | 212 | :options '(git-commit-save-message |
191 | 213 | git-commit-setup-changelog-support |
214 | magit-generate-changelog | |
192 | 215 | git-commit-turn-on-auto-fill |
193 | 216 | git-commit-turn-on-flyspell |
194 | 217 | git-commit-propertize-diff |
308 | 331 | (defface git-commit-keyword |
309 | 332 | '((t :inherit font-lock-string-face)) |
310 | 333 | "Face used for keywords in commit messages. |
311 | In this context a \"keyword\" is text surrounded be brackets." | |
334 | In this context a \"keyword\" is text surrounded by brackets." | |
312 | 335 | :group 'git-commit-faces) |
313 | 336 | |
314 | 337 | (define-obsolete-face-alias 'git-commit-note |
415 | 438 | |
416 | 439 | ;;; Hooks |
417 | 440 | |
418 | ;;;###autoload | |
419 | 441 | (defconst git-commit-filename-regexp "/\\(\ |
420 | 442 | \\(\\(COMMIT\\|NOTES\\|PULLREQ\\|MERGEREQ\\|TAG\\)_EDIT\\|MERGE_\\|\\)MSG\ |
421 | 443 | \\|\\(BRANCH\\|EDIT\\)_DESCRIPTION\\)\\'") |
422 | 444 | |
423 | (eval-after-load 'recentf | |
424 | '(add-to-list 'recentf-exclude git-commit-filename-regexp)) | |
445 | (with-eval-after-load 'recentf | |
446 | (add-to-list 'recentf-exclude git-commit-filename-regexp)) | |
425 | 447 | |
426 | 448 | (add-to-list 'with-editor-file-name-history-exclude git-commit-filename-regexp) |
427 | 449 | |
432 | 454 | |
433 | 455 | (add-hook 'after-change-major-mode-hook 'git-commit-setup-font-lock-in-buffer) |
434 | 456 | |
435 | ;;;###autoload | |
436 | 457 | (defun git-commit-setup-check-buffer () |
437 | 458 | (and buffer-file-name |
438 | 459 | (string-match-p git-commit-filename-regexp buffer-file-name) |
472 | 493 | \\[git-commit-prev-message] and \\[git-commit-next-message] \ |
473 | 494 | to recover older messages") |
474 | 495 | |
475 | ;;;###autoload | |
476 | 496 | (defun git-commit-setup () |
477 | 497 | (when (fboundp 'magit-toplevel) |
478 | 498 | ;; `magit-toplevel' is autoloaded and defined in magit-git.el, |
572 | 592 | |
573 | 593 | (defun git-commit-setup-changelog-support () |
574 | 594 | "Treat ChangeLog entries as unindented paragraphs." |
595 | (when (fboundp 'log-indent-fill-entry) ; New in Emacs 27. | |
596 | (setq-local fill-paragraph-function #'log-indent-fill-entry)) | |
575 | 597 | (setq-local fill-indent-according-to-mode t) |
576 | 598 | (setq-local paragraph-start (concat paragraph-start "\\|\\*\\|("))) |
577 | 599 | |
617 | 639 | "Check for violations of certain basic style conventions. |
618 | 640 | |
619 | 641 | For each violation ask the user if she wants to proceed anyway. |
620 | Option `git-commit-check-style-conventions' controls which | |
642 | Option `git-commit-style-convention-checks' controls which | |
621 | 643 | conventions are checked." |
622 | 644 | (or force |
623 | 645 | (save-excursion |
646 | 668 | "Cycle backward through message history, after saving current message. |
647 | 669 | With a numeric prefix ARG, go back ARG comments." |
648 | 670 | (interactive "*p") |
649 | (when (and (git-commit-save-message) (> arg 0)) | |
650 | (setq log-edit-comment-ring-index | |
651 | (log-edit-new-comment-index | |
652 | arg (ring-length log-edit-comment-ring)))) | |
653 | (save-restriction | |
654 | (goto-char (point-min)) | |
655 | (narrow-to-region (point) | |
656 | (if (re-search-forward (concat "^" comment-start) nil t) | |
657 | (max 1 (- (point) 2)) | |
658 | (point-max))) | |
659 | (log-edit-previous-comment arg))) | |
671 | (let ((len (ring-length log-edit-comment-ring))) | |
672 | (if (<= len 0) | |
673 | (progn (message "Empty comment ring") (ding)) | |
674 | ;; Unlike `log-edit-previous-comment' we save the current | |
675 | ;; non-empty and newly written comment, because otherwise | |
676 | ;; it would be irreversibly lost. | |
677 | (when-let ((message (git-commit-buffer-message))) | |
678 | (unless (ring-member log-edit-comment-ring message) | |
679 | (ring-insert log-edit-comment-ring message) | |
680 | (cl-incf arg) | |
681 | (setq len (ring-length log-edit-comment-ring)))) | |
682 | ;; Delete the message but not the instructions at the end. | |
683 | (save-restriction | |
684 | (goto-char (point-min)) | |
685 | (narrow-to-region | |
686 | (point) | |
687 | (if (re-search-forward (concat "^" comment-start) nil t) | |
688 | (max 1 (- (point) 2)) | |
689 | (point-max))) | |
690 | (delete-region (point-min) (point))) | |
691 | (setq log-edit-comment-ring-index (log-edit-new-comment-index arg len)) | |
692 | (message "Comment %d" (1+ log-edit-comment-ring-index)) | |
693 | (insert (ring-ref log-edit-comment-ring log-edit-comment-ring-index))))) | |
660 | 694 | |
661 | 695 | (defun git-commit-next-message (arg) |
662 | 696 | "Cycle forward through message history, after saving current message. |
695 | 729 | |
696 | 730 | ;;; Headers |
697 | 731 | |
698 | (define-transient-command git-commit-insert-pseudo-header () | |
732 | (transient-define-prefix git-commit-insert-pseudo-header () | |
699 | 733 | "Insert a commit message pseudo header." |
700 | 734 | [["Insert ... by yourself" |
701 | 735 | ("a" "Ack" git-commit-ack) |
851 | 885 | "Changes not staged for commit:" |
852 | 886 | "Unmerged paths:" |
853 | 887 | "Author:" |
854 | "Date:")) | |
888 | "Date:") | |
889 | "Also fontified outside of comments in `git-commit-font-lock-keywords-2'.") | |
855 | 890 | |
856 | 891 | (defconst git-commit-font-lock-keywords-1 |
857 | 892 | '(;; Pseudo headers |
859 | 894 | (regexp-opt git-commit-known-pseudo-headers)) |
860 | 895 | (1 'git-commit-known-pseudo-header) |
861 | 896 | (2 'git-commit-pseudo-header))) |
862 | ("^[-a-zA-Z]+: [^<]+? <[^>]+>" | |
863 | (0 'git-commit-pseudo-header)) | |
864 | 897 | ;; Summary |
865 | 898 | (eval . `(,(git-commit-summary-regexp) |
866 | 899 | (1 'git-commit-summary))) |
886 | 919 | (1 'git-commit-comment-heading t))) |
887 | 920 | (eval . `(,(format "^%s\t\\(?:\\([^:\n]+\\):\\s-+\\)?\\(.*\\)" comment-start) |
888 | 921 | (1 'git-commit-comment-action t t) |
889 | (2 'git-commit-comment-file t))))) | |
922 | (2 'git-commit-comment-file t))) | |
923 | ;; "commit HASH" | |
924 | (eval . `(,(rx bol "commit " (1+ alnum) eol) | |
925 | (0 'git-commit-pseudo-header))) | |
926 | ;; `git-commit-comment-headings' (but not in commented lines) | |
927 | (eval . `(,(rx-to-string `(seq bol (or ,@git-commit-comment-headings) (1+ blank) (1+ nonl) eol)) | |
928 | (0 'git-commit-pseudo-header))))) | |
890 | 929 | |
891 | 930 | (defconst git-commit-font-lock-keywords-3 |
892 | 931 | `(,@git-commit-font-lock-keywords-2 |
904 | 943 | ;; Your branch is up to date with 'master'. |
905 | 944 | ;; Your branch and 'master' have diverged, |
906 | 945 | . `(,(format |
907 | "^%s Your branch \\(?:is up-to-date with\\|and\\) '%s'" | |
946 | "^%s Your branch \\(?:is up[- ]to[- ]date with\\|and\\) '%s'" | |
908 | 947 | comment-start git-commit--branch-name-regexp) |
909 | 948 | (1 'git-commit-comment-branch-local t) |
910 | 949 | (2 'git-commit-comment-branch-remote t))) |
916 | 955 | (1 'bold t) |
917 | 956 | (2 'bold t))))) |
918 | 957 | |
919 | (defvar git-commit-font-lock-keywords git-commit-font-lock-keywords-2 | |
958 | (defvar git-commit-font-lock-keywords git-commit-font-lock-keywords-3 | |
920 | 959 | "Font-Lock keywords for Git-Commit mode.") |
921 | 960 | |
922 | 961 | (defun git-commit-setup-font-lock () |
929 | 968 | (modify-syntax-entry ?` "." table) |
930 | 969 | (set-syntax-table table)) |
931 | 970 | (setq-local comment-start |
932 | (or (ignore-errors | |
933 | (car (process-lines "git" "config" "core.commentchar"))) | |
971 | (or (with-temp-buffer | |
972 | (call-process "git" nil (current-buffer) nil | |
973 | "config" "core.commentchar") | |
974 | (unless (bobp) | |
975 | (goto-char (point-min)) | |
976 | (buffer-substring (point) (line-end-position)))) | |
934 | 977 | "#")) |
935 | 978 | (setq-local comment-start-skip (format "^%s+[\s\t]*" comment-start)) |
936 | 979 | (setq-local comment-end-skip "\n") |
944 | 987 | (progn |
945 | 988 | ;; Make sure the below functions are available. |
946 | 989 | (require 'magit) |
947 | ;; Font-Lock wants every submatch to succeed, | |
948 | ;; so also match the empty string. Do not use | |
949 | ;; `regexp-quote' because that is slow if there | |
950 | ;; are thousands of branches outweighing the | |
951 | ;; benefit of an efficient regep. | |
952 | (format "\\(\\(?:%s\\)\\|\\)\\(\\(?:%s\\)\\|\\)" | |
990 | ;; Font-Lock wants every submatch to succeed, so | |
991 | ;; also match the empty string. Avoid listing | |
992 | ;; remote branches and using `regexp-quote', | |
993 | ;; because in repositories have thousands of | |
994 | ;; branches that would be very slow. See #4353. | |
995 | (format "\\(\\(?:%s\\)\\|\\)\\([^']+\\)" | |
953 | 996 | (mapconcat #'identity |
954 | 997 | (magit-list-local-branch-names) |
955 | "\\|") | |
956 | (mapconcat #'identity | |
957 | (magit-list-remote-branch-names) | |
958 | 998 | "\\|"))) |
959 | 999 | "\\([^']*\\)")) |
960 | 1000 | (setq-local font-lock-multiline t) |
0 | 0 | ;;; git-rebase.el --- Edit Git rebase files -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
7 | 7 | ;; Author: Phil Jackson <phil@shellarchive.co.uk> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | 9 | |
10 | ;; This file is not part of GNU Emacs. | |
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
11 | 11 | |
12 | 12 | ;; This file is free software; you can redistribute it and/or modify |
13 | 13 | ;; it under the terms of the GNU General Public License as published by |
73 | 73 | |
74 | 74 | ;;; Code: |
75 | 75 | |
76 | (require 'dash) | |
76 | (require 'magit) | |
77 | ||
77 | 78 | (require 'easymenu) |
78 | 79 | (require 'server) |
79 | 80 | (require 'with-editor) |
80 | (require 'magit) | |
81 | ||
82 | (and (require 'async-bytecomp nil t) | |
83 | (let ((pkgs (bound-and-true-p async-bytecomp-allowed-packages))) | |
84 | (if (consp pkgs) | |
85 | (cl-intersection '(all magit) pkgs) | |
86 | (memq pkgs '(all t)))) | |
87 | (fboundp 'async-bytecomp-package-mode) | |
88 | (async-bytecomp-package-mode 1)) | |
89 | ||
90 | (eval-when-compile (require 'recentf)) | |
81 | ||
82 | (defvar recentf-exclude) | |
91 | 83 | |
92 | 84 | ;;; Options |
93 | 85 | ;;;; Variables |
222 | 214 | ;;; Commands |
223 | 215 | |
224 | 216 | (defun git-rebase-pick () |
225 | "Use commit on current line." | |
217 | "Use commit on current line. | |
218 | If the region is active, act on all lines touched by the region." | |
226 | 219 | (interactive) |
227 | 220 | (git-rebase-set-action "pick")) |
228 | 221 | |
229 | 222 | (defun git-rebase-reword () |
230 | "Edit message of commit on current line." | |
223 | "Edit message of commit on current line. | |
224 | If the region is active, act on all lines touched by the region." | |
231 | 225 | (interactive) |
232 | 226 | (git-rebase-set-action "reword")) |
233 | 227 | |
234 | 228 | (defun git-rebase-edit () |
235 | "Stop at the commit on the current line." | |
229 | "Stop at the commit on the current line. | |
230 | If the region is active, act on all lines touched by the region." | |
236 | 231 | (interactive) |
237 | 232 | (git-rebase-set-action "edit")) |
238 | 233 | |
239 | 234 | (defun git-rebase-squash () |
240 | "Meld commit on current line into previous commit, edit message." | |
235 | "Meld commit on current line into previous commit, edit message. | |
236 | If the region is active, act on all lines touched by the region." | |
241 | 237 | (interactive) |
242 | 238 | (git-rebase-set-action "squash")) |
243 | 239 | |
244 | 240 | (defun git-rebase-fixup () |
245 | "Meld commit on current line into previous commit, discard its message." | |
241 | "Meld commit on current line into previous commit, discard its message. | |
242 | If the region is active, act on all lines touched by the region." | |
246 | 243 | (interactive) |
247 | 244 | (git-rebase-set-action "fixup")) |
248 | 245 | |
286 | 283 | "r" "reword" |
287 | 284 | "s" "squash") |
288 | 285 | "\\(?1:") |
289 | " \\(?3:[^ \n]+\\) \\(?4:.*\\)")) | |
286 | " \\(?3:[^ \n]+\\) ?\\(?4:.*\\)")) | |
290 | 287 | (exec . "\\(?1:x\\|exec\\) \\(?3:.*\\)") |
291 | 288 | (bare . ,(concat (regexp-opt '("b" "break" "noop") "\\(?1:") |
292 | 289 | " *$")) |
308 | 305 | (goto-char (line-beginning-position)) |
309 | 306 | (if-let ((re-start (concat "^\\(?5:" (regexp-quote comment-start) |
310 | 307 | "\\)? *")) |
311 | (type (-some (lambda (arg) | |
312 | (let ((case-fold-search nil)) | |
313 | (and (looking-at (concat re-start (cdr arg))) | |
314 | (car arg)))) | |
315 | git-rebase-line-regexps))) | |
308 | (type (seq-some (lambda (arg) | |
309 | (let ((case-fold-search nil)) | |
310 | (and (looking-at (concat re-start (cdr arg))) | |
311 | (car arg)))) | |
312 | git-rebase-line-regexps))) | |
316 | 313 | (git-rebase-action |
317 | 314 | :action-type type |
318 | 315 | :action (when-let ((action (match-string-no-properties 1))) |
326 | 323 | (git-rebase-action)))) |
327 | 324 | |
328 | 325 | (defun git-rebase-set-action (action) |
329 | (goto-char (line-beginning-position)) | |
330 | (with-slots (action-type target trailer) | |
331 | (git-rebase-current-line) | |
332 | (if (eq action-type 'commit) | |
333 | (let ((inhibit-read-only t)) | |
334 | (magit-delete-line) | |
335 | (insert (concat action " " target " " trailer "\n")) | |
336 | (unless git-rebase-auto-advance | |
337 | (forward-line -1))) | |
338 | (ding)))) | |
326 | "Set action of commit line to ACTION. | |
327 | If the region is active, operate on all lines that it touches. | |
328 | Otherwise, operate on the current line. As a special case, an | |
329 | ACTION of nil comments the rebase line, regardless of its action | |
330 | type." | |
331 | (pcase (git-rebase-region-bounds t) | |
332 | (`(,beg ,end) | |
333 | (let ((end-marker (copy-marker end)) | |
334 | (pt-below-p (and mark-active (< (mark) (point))))) | |
335 | (set-marker-insertion-type end-marker t) | |
336 | (goto-char beg) | |
337 | (while (< (point) end-marker) | |
338 | (with-slots (action-type target trailer comment-p) | |
339 | (git-rebase-current-line) | |
340 | (cond | |
341 | ((and action (eq action-type 'commit)) | |
342 | (let ((inhibit-read-only t)) | |
343 | (magit-delete-line) | |
344 | (insert (concat action " " target " " trailer "\n")))) | |
345 | ((and action-type (not (or action comment-p))) | |
346 | (let ((inhibit-read-only t)) | |
347 | (insert comment-start " ")) | |
348 | (forward-line)) | |
349 | (t | |
350 | ;; In the case of --rebase-merges, commit lines may have | |
351 | ;; other lines with other action types, empty lines, and | |
352 | ;; "Branch" comments interspersed. Move along. | |
353 | (forward-line))))) | |
354 | (goto-char | |
355 | (if git-rebase-auto-advance | |
356 | end-marker | |
357 | (if pt-below-p (1- end-marker) beg))) | |
358 | (goto-char (line-beginning-position)))) | |
359 | (_ (ding)))) | |
339 | 360 | |
340 | 361 | (defun git-rebase-line-p (&optional pos) |
341 | 362 | (save-excursion |
343 | 364 | (and (oref (git-rebase-current-line) action-type) |
344 | 365 | t))) |
345 | 366 | |
346 | (defun git-rebase-region-bounds () | |
347 | (when (use-region-p) | |
367 | (defun git-rebase-region-bounds (&optional fallback) | |
368 | "Return region bounds if both ends touch rebase lines. | |
369 | Each bound is extended to include the entire line touched by the | |
370 | point or mark. If the region isn't active and FALLBACK is | |
371 | non-nil, return the beginning and end of the current rebase line, | |
372 | if any." | |
373 | (cond | |
374 | ((use-region-p) | |
348 | 375 | (let ((beg (save-excursion (goto-char (region-beginning)) |
349 | 376 | (line-beginning-position))) |
350 | 377 | (end (save-excursion (goto-char (region-end)) |
351 | 378 | (line-end-position)))) |
352 | 379 | (when (and (git-rebase-line-p beg) |
353 | 380 | (git-rebase-line-p end)) |
354 | (list beg (1+ end)))))) | |
381 | (list beg (1+ end))))) | |
382 | ((and fallback (git-rebase-line-p)) | |
383 | (list (line-beginning-position) | |
384 | (1+ (line-end-position)))))) | |
355 | 385 | |
356 | 386 | (defun git-rebase-move-line-down (n) |
357 | 387 | "Move the current commit (or command) N lines down. |
422 | 452 | (funcall (default-value 'redisplay-unhighlight-region-function) rol)) |
423 | 453 | |
424 | 454 | (defun git-rebase-kill-line () |
425 | "Kill the current action line." | |
426 | (interactive) | |
427 | (goto-char (line-beginning-position)) | |
428 | (unless (oref (git-rebase-current-line) comment-p) | |
429 | (let ((inhibit-read-only t)) | |
430 | (insert comment-start) | |
431 | (insert " ")) | |
432 | (goto-char (line-beginning-position)) | |
433 | (when git-rebase-auto-advance | |
434 | (forward-line)))) | |
455 | "Kill the current action line. | |
456 | If the region is active, act on all lines touched by the region." | |
457 | (interactive) | |
458 | (git-rebase-set-action nil)) | |
435 | 459 | |
436 | 460 | (defun git-rebase-insert (rev) |
437 | 461 | "Read an arbitrary commit and insert it below current line." |
793 | 817 | (add-to-list 'with-editor-server-window-alist |
794 | 818 | (cons git-rebase-filename-regexp 'switch-to-buffer)) |
795 | 819 | |
796 | (eval-after-load 'recentf | |
797 | '(add-to-list 'recentf-exclude git-rebase-filename-regexp)) | |
820 | (with-eval-after-load 'recentf | |
821 | (add-to-list 'recentf-exclude git-rebase-filename-regexp)) | |
798 | 822 | |
799 | 823 | (add-to-list 'with-editor-file-name-history-exclude git-rebase-filename-regexp) |
800 | 824 |
0 | 0 | ;;; magit-apply.el --- apply Git diffs -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
28 | 30 | ;; at least at the porcelain level. |
29 | 31 | |
30 | 32 | ;;; Code: |
31 | ||
32 | (eval-when-compile | |
33 | (require 'subr-x)) | |
34 | 33 | |
35 | 34 | (require 'magit-core) |
36 | 35 | (require 'magit-diff) |
52 | 51 | (path &optional prefer-short)) |
53 | 52 | (declare-function borg--maybe-absorb-gitdir "borg" (pkg)) |
54 | 53 | (declare-function borg--sort-submodule-sections "borg" (file)) |
54 | (declare-function borg-assimilate "borg" (package url &optional partially)) | |
55 | 55 | (defvar borg-user-emacs-directory) |
56 | 56 | |
57 | 57 | ;;; Options |
230 | 230 | (ignore-context (magit-diff-ignore-any-space-p))) |
231 | 231 | (unless (magit-diff-context-p) |
232 | 232 | (user-error "Not enough context to apply patch. Increase the context")) |
233 | (when (and magit-wip-before-change-mode (not inhibit-magit-refresh)) | |
233 | (when (and magit-wip-before-change-mode (not magit-inhibit-refresh)) | |
234 | 234 | (magit-wip-commit-before-change files (concat " before " command))) |
235 | 235 | (with-temp-buffer |
236 | 236 | (insert patch) |
238 | 238 | "apply" args "-p0" |
239 | 239 | (and ignore-context "-C0") |
240 | 240 | "--ignore-space-change" "-")) |
241 | (unless inhibit-magit-refresh | |
241 | (unless magit-inhibit-refresh | |
242 | 242 | (when magit-wip-after-apply-mode |
243 | 243 | (magit-wip-commit-after-apply files (concat " after " command))) |
244 | 244 | (magit-refresh)))) |
361 | 361 | `((file . ,repo) (untracked) (status))) |
362 | 362 | start)) |
363 | 363 | (let* ((topdir (magit-toplevel)) |
364 | (url (let ((default-directory | |
365 | (file-name-as-directory (expand-file-name repo)))) | |
366 | (or (magit-get "remote" (magit-get-some-remote) "url") | |
367 | (concat (file-name-as-directory ".") repo)))) | |
364 | 368 | (package |
365 | 369 | (and (equal (bound-and-true-p borg-user-emacs-directory) |
366 | 370 | topdir) |
367 | 371 | (file-name-nondirectory (directory-file-name repo))))) |
368 | (magit-submodule-add-1 | |
369 | (let ((default-directory | |
370 | (file-name-as-directory (expand-file-name repo)))) | |
371 | (or (magit-get "remote" (magit-get-some-remote) "url") | |
372 | (concat (file-name-as-directory ".") repo))) | |
373 | repo | |
374 | (magit-submodule-read-name-for-path repo package)) | |
375 | (when package | |
376 | (borg--sort-submodule-sections | |
377 | (expand-file-name ".gitmodules" topdir)) | |
378 | (let ((default-directory borg-user-emacs-directory)) | |
379 | (borg--maybe-absorb-gitdir package)) | |
380 | (when (and (y-or-n-p | |
381 | (format "Also build and activate `%s' drone?" package)) | |
382 | (fboundp 'borg-build) | |
383 | (fboundp 'borg-activate)) | |
384 | (borg-build package) | |
385 | (borg-activate package)))))) | |
372 | (if (and package | |
373 | (y-or-n-p (format "Also assimilate `%s' drone?" package))) | |
374 | (borg-assimilate package url) | |
375 | (magit-submodule-add-1 | |
376 | url repo (magit-submodule-read-name-for-path repo package)) | |
377 | (when package | |
378 | (borg--sort-submodule-sections | |
379 | (expand-file-name ".gitmodules" topdir)) | |
380 | (let ((default-directory borg-user-emacs-directory)) | |
381 | (borg--maybe-absorb-gitdir package))))))) | |
386 | 382 | (magit-wip-commit-after-apply files " after stage"))) |
387 | 383 | |
388 | 384 | ;;;; Unstage |
485 | 481 | nil (if (magit-file-section-p section) |
486 | 482 | (oref section value) |
487 | 483 | (magit-section-parent-value section))) |
488 | (progn (let ((inhibit-magit-refresh t)) | |
484 | (progn (let ((magit-inhibit-refresh t)) | |
489 | 485 | (funcall apply section "--reverse" "--cached") |
490 | 486 | (funcall apply section "--reverse" "--reject")) |
491 | 487 | (magit-refresh)) |
505 | 501 | nil (if (magit-file-section-p section) |
506 | 502 | (oref section value) |
507 | 503 | (magit-section-parent-value section))) |
508 | (progn (let ((inhibit-magit-refresh t)) | |
504 | (progn (let ((magit-inhibit-refresh t)) | |
509 | 505 | (funcall apply sections "--reverse" "--cached") |
510 | 506 | (funcall apply sections "--reverse" "--reject")) |
511 | 507 | (magit-refresh)) |
543 | 539 | (`(?Y ,_ ?D ) (push file resurrect)) |
544 | 540 | (`(?X ?R ,(or ? ?M ?D)) (push file rename))))) |
545 | 541 | (unwind-protect |
546 | (let ((inhibit-magit-refresh t)) | |
542 | (let ((magit-inhibit-refresh t)) | |
547 | 543 | (magit-wip-commit-before-change files " before discard") |
548 | 544 | (when resolve |
549 | 545 | (magit-discard-files--resolve (nreverse resolve))) |
0 | 0 | ;;; magit-autorevert.el --- revert buffers when files in repository change -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
21 | 23 | ;; along with Magit. If not, see http://www.gnu.org/licenses. |
22 | 24 | |
23 | 25 | ;;; Code: |
24 | ||
25 | (require 'cl-lib) | |
26 | (require 'dash) | |
27 | 26 | |
28 | 27 | (require 'magit-git) |
29 | 28 |
0 | 0 | ;;; magit-bisect.el --- bisect support for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2011-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2011-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
54 | 56 | ;;; Commands |
55 | 57 | |
56 | 58 | ;;;###autoload (autoload 'magit-bisect "magit-bisect" nil t) |
57 | (define-transient-command magit-bisect () | |
59 | (transient-define-prefix magit-bisect () | |
58 | 60 | "Narrow in on the commit that introduced a bug." |
59 | 61 | :man-page "git-bisect" |
60 | ["Actions" | |
62 | [:class transient-subgroups | |
61 | 63 | :if-not magit-bisect-in-progress-p |
62 | ("B" "Start" magit-bisect-start) | |
63 | ("s" "Start script" magit-bisect-run)] | |
64 | ["Arguments" | |
65 | ("-n" "Don't checkout commits" "--no-checkout") | |
66 | ("-p" "Follow only first parent of a merge" "--first-parent" | |
67 | :if (lambda () (version<= "2.29" (magit-git-version)))) | |
68 | (6 magit-bisect:--term-old | |
69 | :if (lambda () (version<= "2.7" (magit-git-version)))) | |
70 | (6 magit-bisect:--term-new | |
71 | :if (lambda () (version<= "2.7" (magit-git-version))))] | |
72 | ["Actions" | |
73 | ("B" "Start" magit-bisect-start) | |
74 | ("s" "Start script" magit-bisect-run)]] | |
64 | 75 | ["Actions" |
65 | 76 | :if magit-bisect-in-progress-p |
66 | 77 | ("B" "Bad" magit-bisect-bad) |
67 | 78 | ("g" "Good" magit-bisect-good) |
79 | (6 "m" "Mark" magit-bisect-mark | |
80 | :if (lambda () (version<= "2.7" (magit-git-version)))) | |
68 | 81 | ("k" "Skip" magit-bisect-skip) |
69 | 82 | ("r" "Reset" magit-bisect-reset) |
70 | 83 | ("s" "Run script" magit-bisect-run)]) |
71 | 84 | |
72 | ;;;###autoload | |
73 | (defun magit-bisect-start (bad good) | |
85 | (transient-define-argument magit-bisect:--term-old () | |
86 | :description "Old/good term" | |
87 | :class 'transient-option | |
88 | :key "=o" | |
89 | :argument "--term-old=") | |
90 | ||
91 | (transient-define-argument magit-bisect:--term-new () | |
92 | :description "New/bad term" | |
93 | :class 'transient-option | |
94 | :key "=n" | |
95 | :argument "--term-new=") | |
96 | ||
97 | ;;;###autoload | |
98 | (defun magit-bisect-start (bad good args) | |
74 | 99 | "Start a bisect session. |
75 | 100 | |
76 | 101 | Bisecting a bug means to find the commit that introduced it. |
77 | This command starts such a bisect session by asking for a know | |
78 | good and a bad commit. To move the session forward use the | |
102 | This command starts such a bisect session by asking for a known | |
103 | good and a known bad commit. To move the session forward use the | |
79 | 104 | other actions from the bisect transient command (\ |
80 | 105 | \\<magit-status-mode-map>\\[magit-bisect])." |
81 | 106 | (interactive (if (magit-bisect-in-progress-p) |
83 | 108 | (magit-bisect-start-read-args))) |
84 | 109 | (unless (magit-rev-ancestor-p good bad) |
85 | 110 | (user-error |
86 | "The good revision (%s) has to be an ancestor of the bad one (%s)" | |
87 | good bad)) | |
111 | "The %s revision (%s) has to be an ancestor of the %s one (%s)" | |
112 | (or (transient-arg-value "--term-old=" args) "good") | |
113 | good | |
114 | (or (transient-arg-value "--term-new=" args) "bad") | |
115 | bad)) | |
88 | 116 | (when (magit-anything-modified-p) |
89 | 117 | (user-error "Cannot bisect with uncommitted changes")) |
90 | (magit-git-bisect "start" (list bad good) t)) | |
118 | (magit-git-bisect "start" (list args bad good) t)) | |
91 | 119 | |
92 | 120 | (defun magit-bisect-start-read-args () |
93 | (let ((b (magit-read-branch-or-commit "Start bisect with bad revision"))) | |
94 | (list b (magit-read-other-branch-or-commit "Good revision" b)))) | |
121 | (let* ((args (transient-args 'magit-bisect)) | |
122 | (bad (magit-read-branch-or-commit | |
123 | (format "Start bisect with %s revision" | |
124 | (or (transient-arg-value "--term-new=" args) | |
125 | "bad"))))) | |
126 | (list bad | |
127 | (magit-read-other-branch-or-commit | |
128 | (format "%s revision" (or (transient-arg-value "--term-old=" args) | |
129 | "Good")) | |
130 | bad) | |
131 | args))) | |
95 | 132 | |
96 | 133 | ;;;###autoload |
97 | 134 | (defun magit-bisect-reset () |
107 | 144 | Use this after you have asserted that the commit does not contain |
108 | 145 | the bug in question." |
109 | 146 | (interactive) |
110 | (magit-git-bisect "good")) | |
147 | (magit-git-bisect (or (cadr (magit-bisect-terms)) | |
148 | (user-error "Not bisecting")))) | |
111 | 149 | |
112 | 150 | ;;;###autoload |
113 | 151 | (defun magit-bisect-bad () |
115 | 153 | Use this after you have asserted that the commit does contain the |
116 | 154 | bug in question." |
117 | 155 | (interactive) |
118 | (magit-git-bisect "bad")) | |
156 | (magit-git-bisect (or (car (magit-bisect-terms)) | |
157 | (user-error "Not bisecting")))) | |
158 | ||
159 | ;;;###autoload | |
160 | (defun magit-bisect-mark () | |
161 | "While bisecting, mark the current commit with a bisect term. | |
162 | During a bisect using alternate terms, commits can still be | |
163 | marked with `magit-bisect-good' and `magit-bisect-bad', as those | |
164 | commands map to the correct term (\"good\" to --term-old's value | |
165 | and \"bad\" to --term-new's). However, in some cases, it can be | |
166 | difficult to keep that mapping straight in your head; this | |
167 | command provides an interface that exposes the underlying terms." | |
168 | (interactive) | |
169 | (magit-git-bisect | |
170 | (pcase-let ((`(,term-new ,term-old) (or (magit-bisect-terms) | |
171 | (user-error "Not bisecting")))) | |
172 | (pcase (read-char-choice | |
173 | (format "Mark HEAD as %s ([n]ew) or %s ([o]ld)" | |
174 | term-new term-old) | |
175 | (list ?n ?o)) | |
176 | (?n term-new) | |
177 | (?o term-old))))) | |
119 | 178 | |
120 | 179 | ;;;###autoload |
121 | 180 | (defun magit-bisect-skip () |
126 | 185 | (magit-git-bisect "skip")) |
127 | 186 | |
128 | 187 | ;;;###autoload |
129 | (defun magit-bisect-run (cmdline &optional bad good) | |
188 | (defun magit-bisect-run (cmdline &optional bad good args) | |
130 | 189 | "Bisect automatically by running commands after each step. |
131 | 190 | |
132 | 191 | Unlike `git bisect run' this can be used before bisecting has |
136 | 195 | (magit-bisect-start-read-args)))) |
137 | 196 | (cons (read-shell-command "Bisect shell command: ") args))) |
138 | 197 | (when (and bad good) |
139 | (magit-bisect-start bad good)) | |
198 | (magit-bisect-start bad good args)) | |
140 | 199 | (magit-git-bisect "run" (list shell-file-name shell-command-switch cmdline))) |
141 | 200 | |
142 | 201 | (defun magit-git-bisect (subcommand &optional args no-assert) |
169 | 228 | (defun magit-bisect-in-progress-p () |
170 | 229 | (file-exists-p (magit-git-dir "BISECT_LOG"))) |
171 | 230 | |
231 | (defun magit-bisect-terms () | |
232 | (magit-file-lines (magit-git-dir "BISECT_TERMS"))) | |
233 | ||
172 | 234 | (defun magit-insert-bisect-output () |
173 | 235 | "While bisecting, insert section with output from `git bisect'." |
174 | 236 | (when (magit-bisect-in-progress-p) |
0 | 0 | ;;; magit-blame.el --- blame support for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2012-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2012-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
26 | 28 | ;; the revision which last modified the line. |
27 | 29 | |
28 | 30 | ;;; Code: |
29 | ||
30 | (eval-when-compile | |
31 | (require 'subr-x)) | |
32 | 31 | |
33 | 32 | (require 'magit) |
34 | 33 | |
55 | 54 | "List of styles used to visualize blame information. |
56 | 55 | |
57 | 56 | Each entry has the form (IDENT (KEY . VALUE)...). IDENT has |
58 | to be a symbol uniquely identifing the style. The following | |
57 | to be a symbol uniquely identifying the style. The following | |
59 | 58 | KEYs are recognized: |
60 | 59 | |
61 | 60 | `show-lines' |
283 | 282 | (define-key map (kbd "q") 'magit-blame-quit) |
284 | 283 | (define-key map (kbd "M-w") 'magit-blame-copy-hash) |
285 | 284 | (define-key map (kbd "SPC") 'magit-diff-show-or-scroll-up) |
285 | (define-key map (kbd "S-SPC") 'magit-diff-show-or-scroll-down) | |
286 | 286 | (define-key map (kbd "DEL") 'magit-diff-show-or-scroll-down) |
287 | 287 | map) |
288 | 288 | "Keymap for `magit-blame-read-only-mode'.") |
473 | 473 | |
474 | 474 | (defun magit-blame--parse-chunk (type) |
475 | 475 | (let (chunk revinfo) |
476 | (looking-at "^\\(.\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)") | |
476 | (unless (looking-at "^\\(.\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)") | |
477 | (error "Blaming failed due to unexpected output: %s" | |
478 | (buffer-substring-no-properties (point) (line-end-position)))) | |
477 | 479 | (with-slots (orig-rev orig-file prev-rev prev-file) |
478 | 480 | (setq chunk (magit-blame-chunk |
479 | 481 | :orig-rev (match-string 1) |
672 | 674 | (propertize |
673 | 675 | (concat (propertize "\s" 'display '(space :height (2))) |
674 | 676 | (propertize "\n" 'line-height t)) |
675 | 'font-lock-face (list :background | |
676 | (face-attribute 'magit-blame-heading | |
677 | :background nil t)))) | |
677 | 'font-lock-face `(:background | |
678 | ,(face-attribute 'magit-blame-heading | |
679 | :background nil t) | |
680 | ,@(and (>= emacs-major-version 27) '(:extend t))))) | |
678 | 681 | |
679 | 682 | (defun magit-blame--format-time-string (time tz) |
680 | 683 | (let* ((time-format (or (magit-blame--style-get 'time-format) |
707 | 710 | ;;; Commands |
708 | 711 | |
709 | 712 | ;;;###autoload (autoload 'magit-blame-echo "magit-blame" nil t) |
710 | (define-suffix-command magit-blame-echo (args) | |
713 | (transient-define-suffix magit-blame-echo (args) | |
711 | 714 | "For each line show the revision in which it was added. |
712 | 715 | Show the information about the chunk at point in the echo area |
713 | 716 | when moving between chunks. Unlike other blaming commands, do |
732 | 735 | (magit-blame--update-overlays))) |
733 | 736 | |
734 | 737 | ;;;###autoload (autoload 'magit-blame-addition "magit-blame" nil t) |
735 | (define-suffix-command magit-blame-addition (args) | |
738 | (transient-define-suffix magit-blame-addition (args) | |
736 | 739 | "For each line show the revision in which it was added." |
737 | 740 | (interactive (list (magit-blame-arguments))) |
738 | 741 | (magit-blame--pre-blame-assert 'addition) |
740 | 743 | (magit-blame--run args)) |
741 | 744 | |
742 | 745 | ;;;###autoload (autoload 'magit-blame-removal "magit-blame" nil t) |
743 | (define-suffix-command magit-blame-removal (args) | |
746 | (transient-define-suffix magit-blame-removal (args) | |
744 | 747 | "For each line show the revision in which it was removed." |
745 | 748 | :if-nil 'buffer-file-name |
746 | 749 | (interactive (list (magit-blame-arguments))) |
751 | 754 | (magit-blame--run args)) |
752 | 755 | |
753 | 756 | ;;;###autoload (autoload 'magit-blame-reverse "magit-blame" nil t) |
754 | (define-suffix-command magit-blame-reverse (args) | |
757 | (transient-define-suffix magit-blame-reverse (args) | |
755 | 758 | "For each line show the last revision in which it still exists." |
756 | 759 | :if-nil 'buffer-file-name |
757 | 760 | (interactive (list (magit-blame-arguments))) |
810 | 813 | (goto-char (point-min)) |
811 | 814 | (forward-line (1- orig-line)))) |
812 | 815 | |
813 | (define-suffix-command magit-blame-quit () | |
816 | (transient-define-suffix magit-blame-quit () | |
814 | 817 | "Turn off Magit-Blame mode. |
815 | 818 | If the buffer was created during a recursive blame, |
816 | 819 | then also kill the buffer." |
884 | 887 | ;;; Popup |
885 | 888 | |
886 | 889 | ;;;###autoload (autoload 'magit-blame "magit-blame" nil t) |
887 | (define-transient-command magit-blame () | |
890 | (transient-define-prefix magit-blame () | |
888 | 891 | "Show the commits that added or removed lines in the visited file." |
889 | 892 | :man-page "git-blame" |
890 | 893 | :value '("-w") |
901 | 904 | ("q" "Quit blaming" magit-blame-quit)] |
902 | 905 | ["Refresh" |
903 | 906 | :if-non-nil magit-blame-mode |
904 | ("c" "Cycle style" magit-blame-cycle-style)]) | |
907 | ("c" "Cycle style" magit-blame-cycle-style :transient t)]) | |
905 | 908 | |
906 | 909 | (defun magit-blame-arguments () |
907 | 910 | (transient-args 'magit-blame)) |
908 | 911 | |
909 | (define-infix-argument magit-blame:-M () | |
912 | (transient-define-argument magit-blame:-M () | |
910 | 913 | :description "Detect lines moved or copied within a file" |
911 | 914 | :class 'transient-option |
912 | 915 | :argument "-M" |
913 | 916 | :reader 'transient-read-number-N+) |
914 | 917 | |
915 | (define-infix-argument magit-blame:-C () | |
918 | (transient-define-argument magit-blame:-C () | |
916 | 919 | :description "Detect lines moved or copied between files" |
917 | 920 | :class 'transient-option |
918 | 921 | :argument "-C" |
0 | 0 | ;;; magit-bookmark.el --- bookmark support for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | 9 | |
10 | 10 | ;; Inspired by an earlier implementation by Yuri Khan. |
11 | ||
12 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
11 | 13 | |
12 | 14 | ;; Magit is free software; you can redistribute it and/or modify it |
13 | 15 | ;; under the terms of the GNU General Public License as published by |
28 | 30 | |
29 | 31 | ;;; Code: |
30 | 32 | |
31 | (eval-when-compile | |
32 | (require 'subr-x)) | |
33 | ||
34 | 33 | (require 'magit) |
35 | 34 | (require 'bookmark) |
36 | 35 | |
42 | 41 | and the buffer-local values of the variables referenced in its |
43 | 42 | `magit-bookmark-variables' property." |
44 | 43 | (if (plist-member (symbol-plist major-mode) 'magit-bookmark-variables) |
45 | (let ((bookmark (bookmark-make-record-default 'no-file))) | |
44 | ;; `bookmark-make-record-default's return value does not match | |
45 | ;; (NAME . ALIST), even though it is used as the default value | |
46 | ;; of `bookmark-make-record-function', which states that such | |
47 | ;; functions must do that. See #4356. | |
48 | (let ((bookmark (cons nil (bookmark-make-record-default 'no-file)))) | |
46 | 49 | (bookmark-prop-set bookmark 'handler 'magit--handle-bookmark) |
47 | 50 | (bookmark-prop-set bookmark 'mode major-mode) |
48 | 51 | (bookmark-prop-set bookmark 'filename (magit-toplevel)) |
86 | 89 | hidden) |
87 | 90 | (magit-section-hide child) |
88 | 91 | (magit-section-show child))))) |
92 | ;; Compatibility with `bookmark+' package. See #4356. | |
93 | (when (bound-and-true-p bmkp-jump-display-function) | |
94 | (funcall bmkp-jump-display-function (current-buffer))) | |
89 | 95 | nil)) |
90 | 96 | |
91 | 97 | (cl-defgeneric magit-bookmark-name () |
0 | 0 | ;;; magit-branch.el --- branch support -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
30 | 32 | |
31 | 33 | ;;; Code: |
32 | 34 | |
33 | (eval-when-compile | |
34 | (require 'subr-x)) | |
35 | ||
36 | 35 | (require 'magit) |
37 | 36 | (require 'magit-reset) |
38 | 37 | |
200 | 199 | ;;; Commands |
201 | 200 | |
202 | 201 | ;;;###autoload (autoload 'magit-branch "magit" nil t) |
203 | (define-transient-command magit-branch (branch) | |
202 | (transient-define-prefix magit-branch (branch) | |
204 | 203 | "Add, configure or remove a branch." |
205 | 204 | :man-page "git-branch" |
205 | ["Arguments" | |
206 | (7 "-r" "Recurse submodules when checking out an existing branch" | |
207 | "--recurse-submodules" | |
208 | :if (lambda () (version<= "2.13" (magit-git-version))))] | |
206 | 209 | ["Variables" |
207 | 210 | :if (lambda () |
208 | 211 | (and magit-branch-direct-configure |
234 | 237 | (interactive (list (magit-get-current-branch))) |
235 | 238 | (transient-setup 'magit-branch nil nil :scope branch)) |
236 | 239 | |
237 | ;;;###autoload | |
238 | (defun magit-checkout (revision) | |
240 | (defun magit-branch-arguments () | |
241 | (transient-args 'magit-branch)) | |
242 | ||
243 | ;;;###autoload | |
244 | (defun magit-checkout (revision &optional args) | |
239 | 245 | "Checkout REVISION, updating the index and the working tree. |
240 | 246 | If REVISION is a local branch, then that becomes the current |
241 | 247 | branch. If it is something else, then `HEAD' becomes detached. |
242 | 248 | Checkout fails if the working tree or the staging area contain |
243 | 249 | changes. |
244 | 250 | \n(git checkout REVISION)." |
245 | (interactive (list (magit-read-other-branch-or-commit "Checkout"))) | |
251 | (interactive (list (magit-read-other-branch-or-commit "Checkout") | |
252 | (magit-branch-arguments))) | |
246 | 253 | (when (string-match "\\`heads/\\(.+\\)" revision) |
247 | 254 | (setq revision (match-string 1 revision))) |
248 | (magit-run-git "checkout" revision)) | |
255 | (magit-run-git "checkout" args revision)) | |
249 | 256 | |
250 | 257 | ;;;###autoload |
251 | 258 | (defun magit-branch-create (branch start-point) |
256 | 263 | (magit-refresh)) |
257 | 264 | |
258 | 265 | ;;;###autoload |
259 | (defun magit-branch-and-checkout (branch start-point) | |
266 | (defun magit-branch-and-checkout (branch start-point &optional args) | |
260 | 267 | "Create and checkout BRANCH at branch or revision START-POINT." |
261 | (interactive (magit-branch-read-args "Create and checkout branch")) | |
268 | (interactive (append (magit-branch-read-args "Create and checkout branch") | |
269 | (list (magit-branch-arguments)))) | |
262 | 270 | (if (string-match-p "^stash@{[0-9]+}$" start-point) |
263 | 271 | (magit-run-git "stash" "branch" branch start-point) |
264 | (magit-call-git "checkout" "-b" branch start-point) | |
272 | (magit-call-git "checkout" args "-b" branch start-point) | |
265 | 273 | (magit-branch-maybe-adjust-upstream branch start-point) |
266 | 274 | (magit-refresh))) |
267 | 275 | |
338 | 346 | (t |
339 | 347 | (list choice (magit-read-starting-point "Create" choice)))))) |
340 | 348 | (if (not start-point) |
341 | (magit-checkout branch) | |
342 | (when (magit-anything-modified-p) | |
349 | (magit-checkout branch (magit-branch-arguments)) | |
350 | (when (magit-anything-modified-p t) | |
343 | 351 | (user-error "Cannot checkout when there are uncommitted changes")) |
344 | 352 | (magit-branch-and-checkout branch start-point) |
345 | 353 | (when (magit-remote-branch-p start-point) |
497 | 505 | (or (and (not (equal branch atpoint)) atpoint) |
498 | 506 | (magit-get-upstream-branch branch))) |
499 | 507 | current-prefix-arg))) |
500 | (let ((inhibit-magit-refresh t)) | |
508 | (let ((magit-inhibit-refresh t)) | |
501 | 509 | (if (equal branch (magit-get-current-branch)) |
502 | 510 | (if (and (magit-anything-modified-p) |
503 | 511 | (not (yes-or-no-p |
561 | 569 | ((string-match "^refs/remotes/\\([^/]+\\)" (car refs)) |
562 | 570 | (let* ((remote (match-string 1 (car refs))) |
563 | 571 | (offset (1+ (length remote)))) |
564 | ;; Assume the branches actually still exists on the remote. | |
565 | (magit-run-git-async | |
566 | "push" | |
567 | (and (or force magit-branch-delete-never-verify) "--no-verify") | |
568 | remote | |
569 | (--map (concat ":" (substring it offset)) branches)) | |
570 | ;; If that is not the case, then this deletes the tracking branches. | |
571 | (set-process-sentinel | |
572 | magit-this-process | |
573 | (apply-partially 'magit-delete-remote-branch-sentinel remote refs)))) | |
572 | (cond | |
573 | ((magit-confirm 'delete-branch-on-remote | |
574 | "Delete %s on the remote (not just locally)" | |
575 | "Delete %i branches on the remote (not just locally)" | |
576 | 'noabort branches) | |
577 | ;; The ref may actually point at another rev on the remote, | |
578 | ;; but this is better than nothing. | |
579 | (dolist (ref refs) | |
580 | (message "Delete %s (was %s)" ref | |
581 | (magit-rev-parse "--short" ref))) | |
582 | ;; Assume the branches actually still exist on the remote. | |
583 | (magit-run-git-async | |
584 | "push" | |
585 | (and (or force magit-branch-delete-never-verify) "--no-verify") | |
586 | remote | |
587 | (--map (concat ":" (substring it offset)) branches)) | |
588 | ;; If that is not the case, then this deletes the tracking branches. | |
589 | (set-process-sentinel | |
590 | magit-this-process | |
591 | (apply-partially 'magit-delete-remote-branch-sentinel remote refs))) | |
592 | (t | |
593 | (dolist (ref refs) | |
594 | (message "Delete %s (was %s)" ref | |
595 | (magit-rev-parse "--short" ref)) | |
596 | (magit-call-git "update-ref" "-d" ref)) | |
597 | (magit-refresh))))) | |
574 | 598 | ((> (length branches) 1) |
575 | 599 | (setq branches (delete (magit-get-current-branch) branches)) |
576 | 600 | (mapc 'magit-branch-maybe-delete-pr-remote branches) |
578 | 602 | (magit-run-git "branch" (if force "-D" "-d") branches)) |
579 | 603 | (t ; And now for something completely different. |
580 | 604 | (let* ((branch (car branches)) |
581 | (prompt (format "Branch %s is checked out. " branch))) | |
605 | (prompt (format "Branch %s is checked out. " branch)) | |
606 | (main (magit-main-branch))) | |
582 | 607 | (when (equal branch (magit-get-current-branch)) |
583 | (pcase (if (or (equal branch "master") | |
584 | (not (magit-rev-verify "master"))) | |
608 | (pcase (if (or (equal branch main) | |
609 | (not main)) | |
585 | 610 | (magit-read-char-case prompt nil |
586 | 611 | (?d "[d]etach HEAD & delete" 'detach) |
587 | 612 | (?a "[a]bort" 'abort)) |
588 | 613 | (magit-read-char-case prompt nil |
589 | (?d "[d]etach HEAD & delete" 'detach) | |
590 | (?c "[c]heckout master & delete" 'master) | |
591 | (?a "[a]bort" 'abort))) | |
614 | (?d "[d]etach HEAD & delete" 'detach) | |
615 | (?c (format "[c]heckout %s & delete" main) 'main) | |
616 | (?a "[a]bort" 'abort))) | |
592 | 617 | (`detach (unless (or (equal force '(4)) |
593 | 618 | (member branch force) |
594 | 619 | (magit-branch-merged-p branch t)) |
596 | 621 | "Delete unmerged branch %s" "" |
597 | 622 | nil (list branch))) |
598 | 623 | (magit-call-git "checkout" "--detach")) |
599 | (`master (unless (or (equal force '(4)) | |
624 | (`main (unless (or (equal force '(4)) | |
600 | 625 | (member branch force) |
601 | (magit-branch-merged-p branch "master")) | |
626 | (magit-branch-merged-p branch main)) | |
602 | 627 | (magit-confirm 'delete-unmerged-branch |
603 | 628 | "Delete unmerged branch %s" "" |
604 | 629 | nil (list branch))) |
605 | (magit-call-git "checkout" "master")) | |
630 | (magit-call-git "checkout" main)) | |
606 | 631 | (`abort (user-error "Abort"))) |
607 | 632 | (setq force t)) |
608 | 633 | (magit-branch-maybe-delete-pr-remote branch) |
685 | 710 | (magit-call-git "branch" (if force "-M" "-m") old new) |
686 | 711 | (when magit-branch-rename-push-target |
687 | 712 | (let ((remote (magit-get-push-remote old)) |
688 | (old-specific (magit-get "branch" old "pushRemote")) | |
689 | (new-specific (magit-get "branch" new "pushRemote"))) | |
690 | (when (and old-specific (or force (not new-specific))) | |
691 | ;; Keep the target setting branch specific, even if that is | |
713 | (old-specified (magit-get "branch" old "pushRemote")) | |
714 | (new-specified (magit-get "branch" new "pushRemote"))) | |
715 | (when (and old-specified (or force (not new-specified))) | |
716 | ;; Keep the target setting branch specified, even if that is | |
692 | 717 | ;; redundant. But if a branch by the same name existed before |
693 | 718 | ;; and the rename isn't forced, then do not change a leftover |
694 | 719 | ;; setting. Such a leftover setting may or may not conform to |
695 | 720 | ;; what we expect here... |
696 | (magit-set old-specific "branch" new "pushRemote")) | |
721 | (magit-set old-specified "branch" new "pushRemote")) | |
697 | 722 | (when (and (equal (magit-get-push-remote new) remote) |
698 | 723 | ;; ...and if it does not, then we must abort. |
699 | 724 | (not (eq magit-branch-rename-push-target 'local-only)) |
761 | 786 | ;;; Configure |
762 | 787 | |
763 | 788 | ;;;###autoload (autoload 'magit-branch-configure "magit-branch" nil t) |
764 | (define-transient-command magit-branch-configure (branch) | |
789 | (transient-define-prefix magit-branch-configure (branch) | |
765 | 790 | "Configure a branch." |
766 | 791 | :man-page "git-branch" |
767 | 792 | [:description |
782 | 807 | (interactive |
783 | 808 | (list (or (and (not current-prefix-arg) |
784 | 809 | (not (and magit-branch-direct-configure |
785 | (eq current-transient-command 'magit-branch))) | |
810 | (eq transient-current-command 'magit-branch))) | |
786 | 811 | (magit-get-current-branch)) |
787 | 812 | (magit--read-branch-scope)))) |
788 | 813 | (transient-setup 'magit-branch-configure nil nil :scope branch)) |
794 | 819 | (format (oref obj variable) "<name>")) |
795 | 820 | "Configure branch"))) |
796 | 821 | |
797 | (define-suffix-command magit-branch.<branch>.description (branch) | |
822 | (transient-define-suffix magit-branch.<branch>.description (branch) | |
798 | 823 | "Edit the description of BRANCH." |
799 | 824 | :class 'magit--git-variable |
800 | 825 | :transient nil |
801 | 826 | :variable "branch.%s.description" |
802 | (interactive (list (oref current-transient-prefix scope))) | |
827 | (interactive (list (oref transient-current-prefix scope))) | |
803 | 828 | (magit-run-git-with-editor "branch" "--edit-description" branch)) |
804 | 829 | |
805 | 830 | (add-hook 'find-file-hook 'magit-branch-description-check-buffers) |
811 | 836 | (defclass magit--git-branch:upstream (magit--git-variable) |
812 | 837 | ((format :initform " %k %m %M\n %r %R"))) |
813 | 838 | |
814 | (define-infix-command magit-branch.<branch>.merge/remote () | |
839 | (transient-define-infix magit-branch.<branch>.merge/remote () | |
815 | 840 | :class 'magit--git-branch:upstream) |
816 | 841 | |
817 | 842 | (cl-defmethod transient-init-value ((obj magit--git-branch:upstream)) |
849 | 874 | (propertize value 'face 'transient-argument) |
850 | 875 | (propertize "unset" 'face 'transient-inactive-argument))) |
851 | 876 | |
852 | (define-infix-command magit-branch.<branch>.rebase () | |
877 | (transient-define-infix magit-branch.<branch>.rebase () | |
853 | 878 | :class 'magit--git-variable:choices |
854 | 879 | :scope 'magit--read-branch-scope |
855 | 880 | :variable "branch.%s.rebase" |
857 | 882 | :choices '("true" "false") |
858 | 883 | :default "false") |
859 | 884 | |
860 | (define-infix-command magit-branch.<branch>.pushRemote () | |
885 | (transient-define-infix magit-branch.<branch>.pushRemote () | |
861 | 886 | :class 'magit--git-variable:choices |
862 | 887 | :scope 'magit--read-branch-scope |
863 | 888 | :variable "branch.%s.pushRemote" |
864 | 889 | :fallback "remote.pushDefault" |
865 | 890 | :choices 'magit-list-remotes) |
866 | 891 | |
867 | (define-infix-command magit-pull.rebase () | |
892 | (transient-define-infix magit-pull.rebase () | |
868 | 893 | :class 'magit--git-variable:choices |
869 | 894 | :variable "pull.rebase" |
870 | 895 | :choices '("true" "false") |
871 | 896 | :default "false") |
872 | 897 | |
873 | (define-infix-command magit-remote.pushDefault () | |
898 | (transient-define-infix magit-remote.pushDefault () | |
874 | 899 | :class 'magit--git-variable:choices |
875 | 900 | :variable "remote.pushDefault" |
876 | 901 | :choices 'magit-list-remotes) |
877 | 902 | |
878 | (define-infix-command magit-branch.autoSetupMerge () | |
903 | (transient-define-infix magit-branch.autoSetupMerge () | |
879 | 904 | :class 'magit--git-variable:choices |
880 | 905 | :variable "branch.autoSetupMerge" |
881 | 906 | :choices '("always" "true" "false") |
882 | 907 | :default "true") |
883 | 908 | |
884 | (define-infix-command magit-branch.autoSetupRebase () | |
909 | (transient-define-infix magit-branch.autoSetupRebase () | |
885 | 910 | :class 'magit--git-variable:choices |
886 | 911 | :variable "branch.autoSetupRebase" |
887 | 912 | :choices '("always" "local" "remote" "never") |
0 | 0 | ;;; magit-clone.el --- clone a repository -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
103 | 105 | ;;; Commands |
104 | 106 | |
105 | 107 | ;;;###autoload (autoload 'magit-clone "magit-clone" nil t) |
106 | (define-transient-command magit-clone (&optional transient) | |
108 | (transient-define-prefix magit-clone (&optional transient) | |
107 | 109 | "Clone a repository." |
108 | 110 | :man-page "git-clone" |
109 | 111 | ["Fetch arguments" |
191 | 193 | (magit-clone-internal repository directory (cons "--mirror" args))) |
192 | 194 | |
193 | 195 | (defun magit-clone-internal (repository directory args) |
194 | (run-hooks 'magit-credential-hook) | |
195 | (setq directory (file-name-as-directory (expand-file-name directory))) | |
196 | (magit-run-git-async "clone" args "--" repository | |
197 | (magit-convert-filename-for-git directory)) | |
198 | ;; Don't refresh the buffer we're calling from. | |
199 | (process-put magit-this-process 'inhibit-refresh t) | |
200 | (set-process-sentinel | |
201 | magit-this-process | |
202 | (lambda (process event) | |
203 | (when (memq (process-status process) '(exit signal)) | |
204 | (let ((magit-process-raise-error t)) | |
205 | (magit-process-sentinel process event))) | |
206 | (when (and (eq (process-status process) 'exit) | |
207 | (= (process-exit-status process) 0)) | |
208 | (unless (memq (car args) '("--bare" "--mirror")) | |
209 | (let ((default-directory directory)) | |
210 | (when (or (eq magit-clone-set-remote.pushDefault t) | |
211 | (and magit-clone-set-remote.pushDefault | |
212 | (y-or-n-p "Set `remote.pushDefault' to \"origin\"? "))) | |
213 | (setf (magit-get "remote.pushDefault") "origin")) | |
214 | (unless magit-clone-set-remote-head | |
215 | (magit-remote-unset-head "origin")))) | |
216 | (with-current-buffer (process-get process 'command-buf) | |
217 | (magit-status-setup-buffer directory)))))) | |
196 | (let* ((checkout (not (memq (car args) '("--bare" "--mirror")))) | |
197 | (remote (or (transient-arg-value "--origin" args) | |
198 | (magit-get "clone.defaultRemote") | |
199 | "origin")) | |
200 | (set-push-default | |
201 | (and checkout | |
202 | (or (eq magit-clone-set-remote.pushDefault t) | |
203 | (and magit-clone-set-remote.pushDefault | |
204 | (y-or-n-p (format "Set `remote.pushDefault' to %S? " | |
205 | remote))))))) | |
206 | (run-hooks 'magit-credential-hook) | |
207 | (setq directory (file-name-as-directory (expand-file-name directory))) | |
208 | (when (file-exists-p directory) | |
209 | (if (file-directory-p directory) | |
210 | (when (> (length (directory-files directory)) 2) | |
211 | (let ((name (magit-clone--url-to-name repository))) | |
212 | (unless (and name | |
213 | (setq directory (file-name-as-directory | |
214 | (expand-file-name name directory))) | |
215 | (not (file-exists-p directory))) | |
216 | (user-error "%s already exists" directory)))) | |
217 | (user-error "%s already exists and is not a directory" directory))) | |
218 | (magit-run-git-async "clone" args "--" repository | |
219 | (magit-convert-filename-for-git directory)) | |
220 | ;; Don't refresh the buffer we're calling from. | |
221 | (process-put magit-this-process 'inhibit-refresh t) | |
222 | (set-process-sentinel | |
223 | magit-this-process | |
224 | (lambda (process event) | |
225 | (when (memq (process-status process) '(exit signal)) | |
226 | (let ((magit-process-raise-error t)) | |
227 | (magit-process-sentinel process event))) | |
228 | (when (and (eq (process-status process) 'exit) | |
229 | (= (process-exit-status process) 0)) | |
230 | (when checkout | |
231 | (let ((default-directory directory)) | |
232 | (when set-push-default | |
233 | (setf (magit-get "remote.pushDefault") remote)) | |
234 | (unless magit-clone-set-remote-head | |
235 | (magit-remote-unset-head remote)))) | |
236 | (with-current-buffer (process-get process 'command-buf) | |
237 | (magit-status-setup-buffer directory))))))) | |
218 | 238 | |
219 | 239 | (defun magit-clone-read-args () |
220 | 240 | (let ((repo (magit-clone-read-repository))) |
225 | 245 | (funcall magit-clone-default-directory repo) |
226 | 246 | magit-clone-default-directory) |
227 | 247 | nil nil |
228 | (and (string-match "\\([^/:]+?\\)\\(/?\\.git\\)?$" repo) | |
229 | (match-string 1 repo))) | |
248 | (magit-clone--url-to-name repo)) | |
230 | 249 | (transient-args 'magit-clone)))) |
231 | 250 | |
232 | 251 | (defun magit-clone-read-repository () |
241 | 260 | (?l "or [l]ocal url" |
242 | 261 | (concat "file://" (read-directory-name "Clone repository: file://"))))) |
243 | 262 | |
263 | (defun magit-clone--url-to-name (url) | |
264 | (and (string-match "\\([^/:]+?\\)\\(/?\\.git\\)?$" url) | |
265 | (match-string 1 url))) | |
266 | ||
244 | 267 | (defun magit-clone--name-to-url (name) |
245 | (or (-some | |
268 | (or (seq-some | |
246 | 269 | (pcase-lambda (`(,re ,host ,user)) |
247 | 270 | (and (string-match re name) |
248 | 271 | (let ((repo (match-string 1 name))) |
0 | 0 | ;;; magit-commit.el --- create Git commits -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
33 | 35 | |
34 | 36 | (eval-when-compile (require 'epa)) ; for `epa-protocol' |
35 | 37 | (eval-when-compile (require 'epg)) |
36 | (eval-when-compile (require 'subr-x)) | |
37 | 38 | |
38 | 39 | ;;; Options |
39 | 40 | |
40 | 41 | (defcustom magit-commit-ask-to-stage 'verbose |
41 | "Whether to ask to stage all unstaged changes when committing and nothing is staged." | |
42 | "Whether to ask to stage everything when committing and nothing is staged." | |
42 | 43 | :package-version '(magit . "2.3.0") |
43 | 44 | :group 'magit-commands |
44 | 45 | :type '(choice (const :tag "Ask" t) |
99 | 100 | ;;; Popup |
100 | 101 | |
101 | 102 | ;;;###autoload (autoload 'magit-commit "magit-commit" nil t) |
102 | (define-transient-command magit-commit () | |
103 | (transient-define-prefix magit-commit () | |
103 | 104 | "Create a new commit or replace an existing commit." |
104 | 105 | :info-manual "(magit)Initiating a Commit" |
105 | 106 | :man-page "git-commit" |
125 | 126 | ("f" "Fixup" magit-commit-fixup) |
126 | 127 | ("s" "Squash" magit-commit-squash) |
127 | 128 | ("A" "Augment" magit-commit-augment) |
128 | (6 "x" "Absorb changes" magit-commit-absorb)] | |
129 | (6 "x" "Absorb changes" magit-commit-autofixup) | |
130 | (6 "X" "Absorb modules" magit-commit-absorb-modules)] | |
129 | 131 | ["" |
130 | 132 | ("F" "Instant fixup" magit-commit-instant-fixup) |
131 | 133 | ("S" "Instant squash" magit-commit-instant-squash)]] |
137 | 139 | (defun magit-commit-arguments nil |
138 | 140 | (transient-args 'magit-commit)) |
139 | 141 | |
140 | (define-infix-argument magit:--gpg-sign () | |
142 | (transient-define-argument magit:--gpg-sign () | |
141 | 143 | :description "Sign using gpg" |
142 | 144 | :class 'transient-option |
143 | 145 | :shortarg "-S" |
144 | 146 | :argument "--gpg-sign=" |
145 | 147 | :allow-empty t |
146 | :reader 'magit-read-gpg-secret-key) | |
148 | :reader 'magit-read-gpg-signing-key) | |
147 | 149 | |
148 | 150 | (defvar magit-gpg-secret-key-hist nil) |
149 | 151 | |
150 | (defun magit-read-gpg-secret-key (prompt &optional initial-input history) | |
152 | (defun magit-read-gpg-secret-key | |
153 | (prompt &optional initial-input history predicate) | |
151 | 154 | (require 'epa) |
152 | (let* ((keys (mapcar | |
153 | (lambda (obj) | |
154 | (let ((key (epg-sub-key-id (car (epg-key-sub-key-list obj)))) | |
155 | (author | |
156 | (when-let ((id-obj (car (epg-key-user-id-list obj)))) | |
157 | (let ((id-str (epg-user-id-string id-obj))) | |
158 | (if (stringp id-str) | |
159 | id-str | |
160 | (epg-decode-dn id-obj)))))) | |
161 | (propertize key 'display (concat key " " author)))) | |
155 | (let* ((keys (mapcan | |
156 | (lambda (cert) | |
157 | (and (or (not predicate) | |
158 | (funcall predicate cert)) | |
159 | (let* ((key (car (epg-key-sub-key-list cert))) | |
160 | (fpr (epg-sub-key-fingerprint key)) | |
161 | (id (epg-sub-key-id key)) | |
162 | (author | |
163 | (when-let ((id-obj | |
164 | (car (epg-key-user-id-list cert)))) | |
165 | (let ((id-str (epg-user-id-string id-obj))) | |
166 | (if (stringp id-str) | |
167 | id-str | |
168 | (epg-decode-dn id-obj)))))) | |
169 | (list | |
170 | (propertize fpr 'display | |
171 | (concat (substring fpr 0 (- (length id))) | |
172 | (propertize id 'face 'highlight) | |
173 | " " author)))))) | |
162 | 174 | (epg-list-keys (epg-make-context epa-protocol) nil t))) |
163 | 175 | (choice (completing-read prompt keys nil nil nil |
164 | 176 | history nil initial-input))) |
165 | 177 | (set-text-properties 0 (length choice) nil choice) |
166 | 178 | choice)) |
167 | 179 | |
168 | (define-infix-argument magit-commit:--reuse-message () | |
180 | (defun magit-read-gpg-signing-key (prompt &optional initial-input history) | |
181 | (magit-read-gpg-secret-key | |
182 | prompt initial-input history | |
183 | (lambda (cert) | |
184 | (cl-some (lambda (key) | |
185 | (memq 'sign (epg-sub-key-capability key))) | |
186 | (epg-key-sub-key-list cert))))) | |
187 | ||
188 | (transient-define-argument magit-commit:--reuse-message () | |
169 | 189 | :description "Reuse commit message" |
170 | 190 | :class 'transient-option |
171 | 191 | :shortarg "-C" |
216 | 236 | (if current-prefix-arg |
217 | 237 | (not magit-commit-extend-override-date) |
218 | 238 | magit-commit-extend-override-date))) |
219 | (when (setq args (magit-commit-assert args (not override-date))) | |
239 | (when (setq args (magit-commit-assert args)) | |
220 | 240 | (magit-commit-amend-assert) |
221 | 241 | (let ((process-environment process-environment)) |
222 | 242 | (unless override-date |
356 | 376 | (and (not strict) |
357 | 377 | ;; ^ For amend variants that don't make sense otherwise. |
358 | 378 | (or (member "--amend" args) |
359 | (member "--allow-empty" args)))) | |
379 | (member "--allow-empty" args) | |
380 | (member "--reset-author" args) | |
381 | (member "--author" args) | |
382 | (member "--signoff" args) | |
383 | (cl-find-if (lambda (a) (string-match-p "\\`--date=" a)) args)))) | |
360 | 384 | (or args (list "--"))) |
361 | 385 | ((and (magit-rebase-in-progress-p) |
362 | 386 | (not (magit-anything-unstaged-p)) |
385 | 409 | (defvar magit--reshelve-history nil) |
386 | 410 | |
387 | 411 | ;;;###autoload |
388 | (defun magit-commit-reshelve (date) | |
412 | (defun magit-commit-reshelve (date update-author &optional args) | |
389 | 413 | "Change the committer date and possibly the author date of `HEAD'. |
390 | 414 | |
391 | If you are the author of `HEAD', then both dates are changed, | |
392 | otherwise only the committer date. The current time is used | |
393 | as the initial minibuffer input and the original author (if | |
394 | that is you) or committer date is available as the previous | |
395 | history element." | |
415 | The current time is used as the initial minibuffer input and the | |
416 | original author or committer date is available as the previous | |
417 | history element. | |
418 | ||
419 | Both the author and the committer dates are changes, unless one | |
420 | of the following is true, in which case only the committer date | |
421 | is updated: | |
422 | - You are not the author of the commit that is being reshelved. | |
423 | - The command was invoked with a prefix argument. | |
424 | - Non-interactively if UPDATE-AUTHOR is nil." | |
396 | 425 | (interactive |
397 | (let ((author-p (magit-rev-author-p "HEAD"))) | |
398 | (push (magit-rev-format (if author-p "%ad" "%cd") "HEAD" | |
426 | (let ((update-author (and (magit-rev-author-p "HEAD") | |
427 | (not current-prefix-arg)))) | |
428 | (push (magit-rev-format (if update-author "%ad" "%cd") "HEAD" | |
399 | 429 | (concat "--date=format:%F %T %z")) |
400 | 430 | magit--reshelve-history) |
401 | (list (read-string (if author-p | |
431 | (list (read-string (if update-author | |
402 | 432 | "Change author and committer dates to: " |
403 | 433 | "Change committer date to: ") |
404 | 434 | (cons (format-time-string "%F %T %z") 17) |
405 | 'magit--reshelve-history)))) | |
435 | 'magit--reshelve-history) | |
436 | update-author | |
437 | (magit-commit-arguments)))) | |
406 | 438 | (let ((process-environment process-environment)) |
407 | 439 | (push (concat "GIT_COMMITTER_DATE=" date) process-environment) |
408 | 440 | (magit-run-git "commit" "--amend" "--no-edit" |
409 | (and (magit-rev-author-p "HEAD") | |
410 | (concat "--date=" date))))) | |
441 | (and update-author (concat "--date=" date)) | |
442 | args))) | |
443 | ||
444 | ;;;###autoload | |
445 | (defun magit-commit-absorb-modules (phase commit) | |
446 | "Spread modified modules across recent commits." | |
447 | (interactive (list 'select (magit-get-upstream-branch))) | |
448 | (let ((modules (magit-list-modified-modules))) | |
449 | (unless modules | |
450 | (user-error "There are no modified modules that could be absorbed")) | |
451 | (when commit | |
452 | (setq commit (magit-rebase-interactive-assert commit t))) | |
453 | (if (and commit (eq phase 'run)) | |
454 | (progn | |
455 | (dolist (module modules) | |
456 | (when-let ((msg (magit-git-string | |
457 | "log" "-1" "--format=%s" | |
458 | (concat commit "..") "--" module))) | |
459 | (magit-git "commit" "-m" (concat "fixup! " msg) | |
460 | "--only" "--" module))) | |
461 | (magit-refresh) | |
462 | t) | |
463 | (magit-log-select | |
464 | (lambda (commit) | |
465 | (magit-commit-absorb-modules 'run commit)) | |
466 | nil nil nil nil commit)))) | |
411 | 467 | |
412 | 468 | ;;;###autoload (autoload 'magit-commit-absorb "magit-commit" nil t) |
413 | (define-transient-command magit-commit-absorb (phase commit args) | |
414 | "Spread unstaged changes across recent commits. | |
469 | (transient-define-prefix magit-commit-absorb (phase commit args) | |
470 | "Spread staged changes across recent commits. | |
415 | 471 | With a prefix argument use a transient command to select infix |
416 | arguments. This command requires the git-autofixup script, which | |
417 | is available from https://github.com/torbiak/git-autofixup." | |
472 | arguments. This command requires git-absorb executable, which | |
473 | is available from https://github.com/tummychow/git-absorb. | |
474 | See `magit-commit-autofixup' for an alternative implementation." | |
418 | 475 | ["Arguments" |
419 | (magit-autofixup:--context) | |
420 | (magit-autofixup:--strict)] | |
476 | ("-f" "Skip safety checks" ("-f" "--force")) | |
477 | ("-v" "Display more output" ("-v" "--verbose"))] | |
421 | 478 | ["Actions" |
422 | 479 | ("x" "Absorb" magit-commit-absorb)] |
423 | 480 | (interactive (if current-prefix-arg |
427 | 484 | (transient-args 'magit-commit-absorb)))) |
428 | 485 | (if (eq phase 'transient) |
429 | 486 | (transient-setup 'magit-commit-absorb) |
487 | (unless (executable-find "git-absorb") | |
488 | (user-error "This command requires the git-absorb executable, which %s" | |
489 | "is available from https://github.com/tummychow/git-absorb")) | |
490 | (unless (magit-anything-staged-p) | |
491 | (if (magit-anything-unstaged-p) | |
492 | (if (y-or-n-p "Nothing staged. Absorb all unstaged changes? ") | |
493 | (magit-with-toplevel | |
494 | (magit-run-git "add" "-u" ".")) | |
495 | (user-error "Abort")) | |
496 | (user-error "There are no changes that could be absorbed"))) | |
497 | (when commit | |
498 | (setq commit (magit-rebase-interactive-assert commit t))) | |
499 | (if (and commit (eq phase 'run)) | |
500 | (progn (magit-run-git-async "absorb" "-v" args "-b" commit) t) | |
501 | (magit-log-select | |
502 | (lambda (commit) | |
503 | (with-no-warnings ; about non-interactive use | |
504 | (magit-commit-absorb 'run commit args))) | |
505 | nil nil nil nil commit)))) | |
506 | ||
507 | ;;;###autoload (autoload 'magit-commit-autofixup "magit-commit" nil t) | |
508 | (transient-define-prefix magit-commit-autofixup (phase commit args) | |
509 | "Spread staged or unstaged changes across recent commits. | |
510 | ||
511 | If there are any staged then spread only those, otherwise | |
512 | spread all unstaged changes. With a prefix argument use a | |
513 | transient command to select infix arguments. | |
514 | ||
515 | This command requires the git-autofixup script, which is | |
516 | available from https://github.com/torbiak/git-autofixup. | |
517 | See `magit-commit-absorb' for an alternative implementation." | |
518 | ["Arguments" | |
519 | (magit-autofixup:--context) | |
520 | (magit-autofixup:--strict)] | |
521 | ["Actions" | |
522 | ("x" "Absorb" magit-commit-autofixup)] | |
523 | (interactive (if current-prefix-arg | |
524 | (list 'transient nil nil) | |
525 | (list 'select | |
526 | (magit-get-upstream-branch) | |
527 | (transient-args 'magit-commit-autofixup)))) | |
528 | (if (eq phase 'transient) | |
529 | (transient-setup 'magit-commit-autofixup) | |
430 | 530 | (unless (executable-find "git-autofixup") |
431 | 531 | (user-error "This command requires the git-autofixup script, which %s" |
432 | 532 | "is available from https://github.com/torbiak/git-autofixup")) |
433 | (when (magit-anything-staged-p) | |
434 | (user-error "Cannot absorb when there are staged changes")) | |
435 | (unless (magit-anything-unstaged-p) | |
436 | (user-error "There are no unstaged changes that could be absorbed")) | |
533 | (unless (magit-anything-modified-p) | |
534 | (user-error "There are no changes that could be absorbed")) | |
437 | 535 | (when commit |
438 | 536 | (setq commit (magit-rebase-interactive-assert commit t))) |
439 | 537 | (if (and commit (eq phase 'run)) |
441 | 539 | (magit-log-select |
442 | 540 | (lambda (commit) |
443 | 541 | (with-no-warnings ; about non-interactive use |
444 | (magit-commit-absorb 'run commit args))) | |
542 | (magit-commit-autofixup 'run commit args))) | |
445 | 543 | nil nil nil nil commit)))) |
446 | 544 | |
447 | (define-infix-argument magit-autofixup:--context () | |
545 | (transient-define-argument magit-autofixup:--context () | |
448 | 546 | :description "Diff context lines" |
449 | 547 | :class 'transient-option |
450 | 548 | :shortarg "-c" |
451 | 549 | :argument "--context=" |
452 | 550 | :reader 'transient-read-number-N0) |
453 | 551 | |
454 | (define-infix-argument magit-autofixup:--strict () | |
552 | (transient-define-argument magit-autofixup:--strict () | |
455 | 553 | :description "Strictness" |
456 | 554 | :class 'transient-option |
457 | 555 | :shortarg "-s" |
471 | 569 | (let ((args (car (magit-diff-arguments))) |
472 | 570 | (magit-inhibit-save-previous-winconf 'unset) |
473 | 571 | (magit-display-buffer-noselect t) |
474 | (inhibit-quit nil)) | |
572 | (inhibit-quit nil) | |
573 | (display-buffer-overriding-action '(nil (inhibit-same-window t)))) | |
475 | 574 | (message "Diffing changes to be committed (C-g to abort diffing)") |
476 | 575 | (cl-case last-command |
477 | 576 | (magit-commit |
490 | 589 | ;; Mention `magit-diff-while-committing' because that's |
491 | 590 | ;; always what I search for when I try to find this line. |
492 | 591 | (add-hook 'server-switch-hook 'magit-commit-diff) |
592 | (add-hook 'with-editor-filter-visit-hook 'magit-commit-diff) | |
493 | 593 | |
494 | 594 | (add-to-list 'with-editor-server-window-alist |
495 | 595 | (cons git-commit-filename-regexp 'switch-to-buffer)) |
0 | 0 | ;;; magit-core.el --- core functionality -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
0 | 0 | ;;; magit-diff.el --- inspect Git diffs -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
27 | 29 | |
28 | 30 | ;;; Code: |
29 | 31 | |
30 | (eval-when-compile | |
31 | (require 'ansi-color) | |
32 | (require 'subr-x)) | |
33 | ||
32 | (require 'magit-core) | |
34 | 33 | (require 'git-commit) |
35 | (require 'magit-core) | |
34 | ||
35 | (eval-when-compile (require 'ansi-color)) | |
36 | (require 'diff-mode) | |
37 | (require 'smerge-mode) | |
36 | 38 | |
37 | 39 | ;; For `magit-diff-popup' |
38 | 40 | (declare-function magit-stash-show "magit-stash" (stash &optional args files)) |
54 | 56 | (declare-function magit-merge-in-progress-p "magit-merge" ()) |
55 | 57 | (declare-function magit--merge-range "magit-merge" (&optional head)) |
56 | 58 | ;; For `magit-diff--dwim' |
59 | (declare-function forge--pullreq-range "forge-pullreq" | |
60 | (pullreq &optional endpoints)) | |
57 | 61 | (declare-function forge--pullreq-ref "forge-pullreq" (pullreq)) |
58 | 62 | ;; For `magit-diff-wash-diff' |
59 | 63 | (declare-function ansi-color-apply-on-region "ansi-color" (begin end)) |
60 | 64 | |
61 | 65 | (eval-when-compile |
62 | (cl-pushnew 'base-ref eieio--known-slot-names) | |
63 | 66 | (cl-pushnew 'orig-rev eieio--known-slot-names) |
64 | 67 | (cl-pushnew 'action-type eieio--known-slot-names) |
65 | 68 | (cl-pushnew 'target eieio--known-slot-names)) |
66 | 69 | |
67 | (require 'diff-mode) | |
68 | (require 'smerge-mode) | |
69 | ||
70 | 70 | ;;; Options |
71 | 71 | ;;;; Diff Mode |
72 | 72 | |
73 | 73 | (defgroup magit-diff nil |
74 | 74 | "Inspect and manipulate Git diffs." |
75 | 75 | :link '(info-link "(magit)Diffing") |
76 | :group 'magit-commands | |
76 | 77 | :group 'magit-modes) |
77 | 78 | |
78 | 79 | (defcustom magit-diff-mode-hook nil |
280 | 281 | :group 'magit-diff |
281 | 282 | :type 'boolean) |
282 | 283 | |
284 | (defcustom magit-diff-extra-stat-arguments nil | |
285 | "Additional arguments to be used alongside `--stat'. | |
286 | ||
287 | A list of zero or more arguments or a function that takes no | |
288 | argument and returns such a list. These arguments are allowed | |
289 | here: `--stat-width', `--stat-name-width', `--stat-graph-width' | |
290 | and `--compact-summary'. See the git-diff(1) manpage." | |
291 | :package-version '(magit . "3.0.0") | |
292 | :group 'magit-diff | |
293 | :type '(radio (function-item magit-diff-use-window-width-as-stat-width) | |
294 | function | |
295 | (list string) | |
296 | (const :tag "None" nil))) | |
297 | ||
283 | 298 | ;;;; File Diff |
284 | 299 | |
285 | 300 | (defcustom magit-diff-buffer-file-locked t |
296 | 311 | :link '(info-link "(magit)Revision Buffer") |
297 | 312 | :group 'magit-modes) |
298 | 313 | |
299 | (defcustom magit-revision-mode-hook '(bug-reference-mode) | |
314 | (defcustom magit-revision-mode-hook | |
315 | '(bug-reference-mode | |
316 | goto-address-mode) | |
300 | 317 | "Hook run after entering Magit-Revision mode." |
301 | 318 | :group 'magit-revision |
302 | 319 | :type 'hook |
303 | :options '(bug-reference-mode)) | |
320 | :options '(bug-reference-mode | |
321 | goto-address-mode)) | |
304 | 322 | |
305 | 323 | (defcustom magit-revision-sections-hook |
306 | 324 | '(magit-insert-revision-tag |
743 | 761 | (pcase-let ((`(,args ,files) |
744 | 762 | (magit-diff--get-value 'magit-diff-mode |
745 | 763 | magit-prefix-use-buffer-arguments))) |
746 | (unless (eq current-transient-command 'magit-dispatch) | |
764 | (unless (eq transient-current-command 'magit-dispatch) | |
747 | 765 | (when-let ((file (magit-file-relative-name))) |
748 | 766 | (setq files (list file)))) |
749 | 767 | (oset obj value (if files `(("--" ,@files) ,args) args)))) |
764 | 782 | |
765 | 783 | (defun magit-diff-arguments (&optional mode) |
766 | 784 | "Return the current diff arguments." |
767 | (if (memq current-transient-command '(magit-diff magit-diff-refresh)) | |
785 | (if (memq transient-current-command '(magit-diff magit-diff-refresh)) | |
768 | 786 | (pcase-let ((`(,args ,alist) |
769 | 787 | (-separate #'atom (transient-get-value)))) |
770 | 788 | (list args (cdr (assoc "--" alist)))) |
815 | 833 | ;;; Section Classes |
816 | 834 | |
817 | 835 | (defclass magit-file-section (magit-section) |
818 | ((source :initform nil) | |
819 | (header :initform nil))) | |
836 | ((keymap :initform magit-file-section-map) | |
837 | (source :initform nil) | |
838 | (header :initform nil))) | |
820 | 839 | |
821 | 840 | (defclass magit-module-section (magit-file-section) |
822 | ()) | |
841 | ((keymap :initform magit-hunk-section-map))) | |
823 | 842 | |
824 | 843 | (defclass magit-hunk-section (magit-section) |
825 | ((refined :initform nil) | |
844 | ((keymap :initform magit-hunk-section-map) | |
845 | (refined :initform nil) | |
826 | 846 | (combined :initform nil) |
827 | 847 | (from-range :initform nil) |
828 | 848 | (from-ranges :initform nil) |
837 | 857 | ;;;; Prefix Commands |
838 | 858 | |
839 | 859 | ;;;###autoload (autoload 'magit-diff "magit-diff" nil t) |
840 | (define-transient-command magit-diff () | |
860 | (transient-define-prefix magit-diff () | |
841 | 861 | "Show changes between different versions." |
842 | 862 | :man-page "git-diff" |
843 | 863 | :class 'magit-diff-prefix |
870 | 890 | ("t" "Show stash" magit-stash-show)]]) |
871 | 891 | |
872 | 892 | ;;;###autoload (autoload 'magit-diff-refresh "magit-diff" nil t) |
873 | (define-transient-command magit-diff-refresh () | |
893 | (transient-define-prefix magit-diff-refresh () | |
874 | 894 | "Change the arguments used for the diff(s) in the current buffer." |
875 | 895 | :man-page "git-diff" |
876 | 896 | :class 'magit-diff-refresh-prefix |
908 | 928 | ("r" "switch range type" magit-diff-switch-range-type) |
909 | 929 | ("f" "flip revisions" magit-diff-flip-revs)]] |
910 | 930 | (interactive) |
911 | (if (not (eq current-transient-command 'magit-diff-refresh)) | |
931 | (if (not (eq transient-current-command 'magit-diff-refresh)) | |
912 | 932 | (transient-setup 'magit-diff-refresh) |
913 | 933 | (pcase-let ((`(,args ,files) (magit-diff-arguments))) |
914 | 934 | (setq magit-buffer-diff-args args) |
917 | 937 | |
918 | 938 | ;;;; Infix Commands |
919 | 939 | |
920 | (define-infix-argument magit:-- () | |
940 | (transient-define-argument magit:-- () | |
921 | 941 | :description "Limit to files" |
922 | 942 | :class 'transient-files |
923 | 943 | :key "--" |
929 | 949 | (defun magit-read-files (prompt initial-input history) |
930 | 950 | (magit-completing-read-multiple* prompt |
931 | 951 | (magit-list-files) |
932 | nil nil initial-input history)) | |
933 | ||
934 | (define-infix-argument magit-diff:-U () | |
952 | nil nil | |
953 | (or initial-input (magit-file-at-point)) | |
954 | history)) | |
955 | ||
956 | (transient-define-argument magit-diff:-U () | |
935 | 957 | :description "Context lines" |
936 | 958 | :class 'transient-option |
937 | 959 | :argument "-U" |
938 | :reader 'transient-read-number-N+) | |
939 | ||
940 | (define-infix-argument magit-diff:-M () | |
960 | :reader 'transient-read-number-N0) | |
961 | ||
962 | (transient-define-argument magit-diff:-M () | |
941 | 963 | :description "Detect renames" |
942 | 964 | :class 'transient-option |
943 | 965 | :argument "-M" |
944 | 966 | :reader 'transient-read-number-N+) |
945 | 967 | |
946 | (define-infix-argument magit-diff:-C () | |
968 | (transient-define-argument magit-diff:-C () | |
947 | 969 | :description "Detect copies" |
948 | 970 | :class 'transient-option |
949 | 971 | :argument "-C" |
950 | 972 | :reader 'transient-read-number-N+) |
951 | 973 | |
952 | (define-infix-argument magit-diff:--diff-algorithm () | |
974 | (transient-define-argument magit-diff:--diff-algorithm () | |
953 | 975 | :description "Diff algorithm" |
954 | 976 | :class 'transient-option |
955 | 977 | :key "-A" |
963 | 985 | (?p "[p]atience" "patience") |
964 | 986 | (?h "[h]istogram" "histogram"))) |
965 | 987 | |
966 | (define-infix-argument magit-diff:--ignore-submodules () | |
988 | (transient-define-argument magit-diff:--ignore-submodules () | |
967 | 989 | :description "Ignore submodules" |
968 | 990 | :class 'transient-option |
969 | 991 | :key "-i" |
976 | 998 | (?d "[d]irty" "dirty") |
977 | 999 | (?a "[a]ll" "all"))) |
978 | 1000 | |
979 | (define-infix-argument magit-diff:--color-moved () | |
1001 | (transient-define-argument magit-diff:--color-moved () | |
980 | 1002 | :description "Color moved lines" |
981 | 1003 | :class 'transient-option |
982 | 1004 | :key "-m" |
991 | 1013 | (?z "[z]ebra" "zebra") |
992 | 1014 | (?Z "[Z] dimmed-zebra" "dimmed-zebra"))) |
993 | 1015 | |
994 | (define-infix-argument magit-diff:--color-moved-ws () | |
1016 | (transient-define-argument magit-diff:--color-moved-ws () | |
995 | 1017 | :description "Whitespace treatment for --color-moved" |
996 | 1018 | :class 'transient-option |
997 | 1019 | :key "=w" |
1055 | 1077 | (cons 'commit |
1056 | 1078 | (magit-section-case |
1057 | 1079 | (commit (oref it value)) |
1058 | (file (-> it | |
1059 | (oref parent) | |
1060 | (oref value))) | |
1061 | (hunk (-> it | |
1062 | (oref parent) | |
1063 | (oref parent) | |
1064 | (oref value)))))) | |
1080 | (file (thread-first it | |
1081 | (oref parent) | |
1082 | (oref value))) | |
1083 | (hunk (thread-first it | |
1084 | (oref parent) | |
1085 | (oref parent) | |
1086 | (oref value)))))) | |
1065 | 1087 | ((derived-mode-p 'magit-revision-mode) |
1066 | 1088 | (cons 'commit magit-buffer-revision)) |
1067 | 1089 | ((derived-mode-p 'magit-diff-mode) |
1086 | 1108 | atpoint)))) |
1087 | 1109 | (commit (cons 'commit (oref it value))) |
1088 | 1110 | (stash (cons 'stash (oref it value))) |
1089 | (pullreq (let ((pullreq (oref it value))) | |
1090 | (format "%s...%s" | |
1091 | (oref pullreq base-ref) | |
1092 | (forge--pullreq-ref pullreq)))))))) | |
1111 | (pullreq (forge--pullreq-range (oref it value) t)))))) | |
1093 | 1112 | |
1094 | 1113 | (defun magit-diff-read-range-or-commit (prompt &optional secondary-default mbase) |
1095 | 1114 | "Read range or revision with special diff range treatment. |
1267 | 1286 | (atpoint (or (and (bound-and-true-p magit-blame-mode) |
1268 | 1287 | (oref (magit-current-blame-chunk) orig-rev)) |
1269 | 1288 | mcommit |
1289 | (thing-at-point 'git-revision t) | |
1270 | 1290 | (magit-branch-or-commit-at-point))) |
1271 | 1291 | (`(,args ,files) (magit-show-commit--arguments))) |
1272 | 1292 | (list (or (and (not current-prefix-arg) atpoint) |
1330 | 1350 | (magit-section-update-highlight) |
1331 | 1351 | t)) |
1332 | 1352 | |
1333 | (cl-defmethod magit-buffer-value (&context (major-mode magit-revision-mode)) | |
1334 | (cons magit-buffer-range magit-buffer-diff-files)) | |
1335 | ||
1336 | 1353 | ;;;; Setting Commands |
1337 | 1354 | |
1338 | 1355 | (defun magit-diff-switch-range-type () |
1377 | 1394 | (cl-rotatef magit-buffer-diff-files |
1378 | 1395 | magit-buffer-diff-files-suspended) |
1379 | 1396 | (setq magit-buffer-diff-files |
1380 | (magit-read-files "Limit to file(s): " | |
1381 | (magit-file-at-point) | |
1382 | nil))) | |
1397 | (transient-infix-read 'magit:--))) | |
1383 | 1398 | (magit-refresh))) |
1384 | 1399 | (cond |
1385 | 1400 | ((derived-mode-p 'magit-log-mode |
1396 | 1411 | (defun magit-diff-less-context (&optional count) |
1397 | 1412 | "Decrease the context for diff hunks by COUNT lines." |
1398 | 1413 | (interactive "p") |
1399 | (magit-diff-set-context `(lambda (cur) (max 0 (- (or cur 0) ,count))))) | |
1414 | (magit-diff-set-context (lambda (cur) (max 0 (- (or cur 0) count))))) | |
1400 | 1415 | |
1401 | 1416 | (defun magit-diff-more-context (&optional count) |
1402 | 1417 | "Increase the context for diff hunks by COUNT lines." |
1403 | 1418 | (interactive "p") |
1404 | (magit-diff-set-context `(lambda (cur) (+ (or cur 0) ,count)))) | |
1419 | (magit-diff-set-context (lambda (cur) (+ (or cur 0) count)))) | |
1405 | 1420 | |
1406 | 1421 | (defun magit-diff-default-context () |
1407 | 1422 | "Reset context for diff hunks to the default height." |
1553 | 1568 | (defun magit-diff-visit-file--internal (file force-worktree fn) |
1554 | 1569 | "From a diff visit the appropriate version of FILE. |
1555 | 1570 | If FORCE-WORKTREE is non-nil, then visit the worktree version of |
1556 | the file, even if the diff is about a committed change. USE FN | |
1571 | the file, even if the diff is about a committed change. Use FN | |
1557 | 1572 | to display the buffer in some window." |
1558 | 1573 | (if (magit-file-accessible-directory-p file) |
1559 | 1574 | (magit-diff-visit-directory file force-worktree) |
1699 | 1714 | (cdr spec) |
1700 | 1715 | (cdr (magit-split-range spec))))) |
1701 | 1716 | (if (and magit-diff-visit-avoid-head-blob |
1702 | (magit-rev-head-p spec)) | |
1717 | (magit-rev-head-p rev)) | |
1703 | 1718 | 'unstaged |
1704 | 1719 | rev)))) |
1705 | 1720 | |
1827 | 1842 | (cond ((and (--any-p (oref it hidden) children) |
1828 | 1843 | (--any-p (oref it children) children)) |
1829 | 1844 | (mapc 'magit-section-show-headings sections)) |
1830 | ((-any-p 'magit-section-hidden-body children) | |
1845 | ((seq-some 'magit-section-hidden-body children) | |
1831 | 1846 | (mapc 'magit-section-show-children sections)) |
1832 | 1847 | (t |
1833 | 1848 | (mapc 'magit-section-hide sections))))))) |
1884 | 1899 | (magit-buffer-range range) |
1885 | 1900 | (magit-buffer-typearg typearg) |
1886 | 1901 | (magit-buffer-diff-args args) |
1887 | (magit-buffer-diff-files files))) | |
1902 | (magit-buffer-diff-files files) | |
1903 | (magit-buffer-diff-files-suspended nil))) | |
1888 | 1904 | |
1889 | 1905 | (defun magit-diff-refresh-buffer () |
1890 | 1906 | "Refresh the current `magit-diff-mode' buffer." |
1918 | 1934 | (list 'unstaged magit-buffer-typearg))) |
1919 | 1935 | (and magit-buffer-diff-files (cons "--" magit-buffer-diff-files)))) |
1920 | 1936 | |
1921 | (defvar magit-file-section-map | |
1937 | (defvar magit-diff-section-base-map | |
1922 | 1938 | (let ((map (make-sparse-keymap))) |
1923 | (define-key map (kbd "C-j") 'magit-diff-visit-worktree-file) | |
1924 | (define-key map [C-return] 'magit-diff-visit-worktree-file) | |
1939 | (define-key map (kbd "C-j") 'magit-diff-visit-worktree-file) | |
1940 | (define-key map (kbd "C-<return>") 'magit-diff-visit-worktree-file) | |
1941 | (define-key map (kbd "C-x 4 <return>") 'magit-diff-visit-file-other-window) | |
1942 | (define-key map (kbd "C-x 5 <return>") 'magit-diff-visit-file-other-frame) | |
1925 | 1943 | (define-key map [remap magit-visit-thing] 'magit-diff-visit-file) |
1926 | 1944 | (define-key map [remap magit-delete-thing] 'magit-discard) |
1927 | 1945 | (define-key map [remap magit-revert-no-commit] 'magit-reverse) |
1928 | 1946 | (define-key map "a" 'magit-apply) |
1929 | (define-key map "C" 'magit-commit-add-log) | |
1930 | 1947 | (define-key map "s" 'magit-stage) |
1931 | 1948 | (define-key map "u" 'magit-unstage) |
1932 | 1949 | (define-key map "&" 'magit-do-async-shell-command) |
1933 | (define-key map "\C-c\C-t" 'magit-diff-trace-definition) | |
1934 | (define-key map "\C-c\C-e" 'magit-diff-edit-hunk-commit) | |
1950 | (define-key map "C" 'magit-commit-add-log) | |
1951 | (define-key map (kbd "C-x a") 'magit-add-change-log-entry) | |
1952 | (define-key map (kbd "C-x 4 a") 'magit-add-change-log-entry-other-window) | |
1953 | (define-key map (kbd "C-c C-t") 'magit-diff-trace-definition) | |
1954 | (define-key map (kbd "C-c C-e") 'magit-diff-edit-hunk-commit) | |
1955 | map) | |
1956 | "Parent of `magit-{hunk,file}-section-map'.") | |
1957 | ||
1958 | (defvar magit-file-section-map | |
1959 | (let ((map (make-sparse-keymap))) | |
1960 | (set-keymap-parent map magit-diff-section-base-map) | |
1935 | 1961 | map) |
1936 | 1962 | "Keymap for `file' sections.") |
1937 | 1963 | |
1938 | 1964 | (defvar magit-hunk-section-map |
1939 | 1965 | (let ((map (make-sparse-keymap))) |
1940 | (define-key map (kbd "C-j") 'magit-diff-visit-worktree-file) | |
1941 | (define-key map [C-return] 'magit-diff-visit-worktree-file) | |
1942 | (define-key map [remap magit-visit-thing] 'magit-diff-visit-file) | |
1943 | (define-key map [remap magit-delete-thing] 'magit-discard) | |
1944 | (define-key map [remap magit-revert-no-commit] 'magit-reverse) | |
1945 | (define-key map "a" 'magit-apply) | |
1946 | (define-key map "C" 'magit-commit-add-log) | |
1947 | (define-key map "s" 'magit-stage) | |
1948 | (define-key map "u" 'magit-unstage) | |
1949 | (define-key map "&" 'magit-do-async-shell-command) | |
1950 | (define-key map "\C-c\C-t" 'magit-diff-trace-definition) | |
1951 | (define-key map "\C-c\C-e" 'magit-diff-edit-hunk-commit) | |
1966 | (set-keymap-parent map magit-diff-section-base-map) | |
1952 | 1967 | map) |
1953 | 1968 | "Keymap for `hunk' sections.") |
1954 | 1969 | |
1970 | (defconst magit-diff-conflict-headline-re | |
1971 | (concat "^" (regexp-opt | |
1972 | ;; Defined in merge-tree.c in this order. | |
1973 | '("merged" | |
1974 | "added in remote" | |
1975 | "added in both" | |
1976 | "added in local" | |
1977 | "removed in both" | |
1978 | "changed in both" | |
1979 | "removed in local" | |
1980 | "removed in remote")))) | |
1981 | ||
1955 | 1982 | (defconst magit-diff-headline-re |
1956 | 1983 | (concat "^\\(@@@?\\|diff\\|Submodule\\|" |
1957 | "\\* Unmerged path\\|merged\\|changed in both\\|" | |
1958 | "added in remote\\|removed in remote\\)")) | |
1984 | "\\* Unmerged path\\|" | |
1985 | (substring magit-diff-conflict-headline-re 1) | |
1986 | "\\)")) | |
1959 | 1987 | |
1960 | 1988 | (defconst magit-diff-statline-re |
1961 | 1989 | (concat "^ ?" |
1992 | 2020 | |
1993 | 2021 | (defun magit--insert-diff (&rest args) |
1994 | 2022 | (declare (indent 0)) |
1995 | (let ((magit-git-global-arguments | |
1996 | (remove "--literal-pathspecs" magit-git-global-arguments))) | |
1997 | (setq args (-flatten args)) | |
2023 | (pcase-let ((`(,cmd . ,args) | |
2024 | (-flatten args)) | |
2025 | (magit-git-global-arguments | |
2026 | (remove "--literal-pathspecs" magit-git-global-arguments))) | |
1998 | 2027 | ;; As of Git 2.19.0, we need to generate diffs with |
1999 | 2028 | ;; --ita-visible-in-index so that `magit-stage' can work with |
2000 | 2029 | ;; intent-to-add files (see #4026). Cache the result for each |
2001 | 2030 | ;; repo to avoid a `git version' call for every diff insertion. |
2002 | (when (pcase (magit-repository-local-get 'diff-ita-kludge-p 'unset) | |
2003 | (`unset | |
2004 | (let ((val (version<= "2.19.0" (magit-git-version)))) | |
2005 | (magit-repository-local-set 'diff-ita-kludge-p val) | |
2006 | val)) | |
2007 | (val val)) | |
2008 | (push "--ita-visible-in-index" (cdr args))) | |
2031 | (when (and (not (equal cmd "merge-tree")) | |
2032 | (pcase (magit-repository-local-get 'diff-ita-kludge-p 'unset) | |
2033 | (`unset | |
2034 | (let ((val (version<= "2.19.0" (magit-git-version)))) | |
2035 | (magit-repository-local-set 'diff-ita-kludge-p val) | |
2036 | val)) | |
2037 | (val val))) | |
2038 | (push "--ita-visible-in-index" args)) | |
2039 | (setq args (magit-diff--maybe-add-stat-arguments args)) | |
2009 | 2040 | (when (cl-member-if (lambda (arg) (string-prefix-p "--color-moved" arg)) args) |
2010 | (push "--color=always" (cdr args)) | |
2041 | (push "--color=always" args) | |
2011 | 2042 | (setq magit-git-global-arguments |
2012 | 2043 | (append magit-diff--reset-non-color-moved |
2013 | 2044 | magit-git-global-arguments))) |
2014 | (magit-git-wash #'magit-diff-wash-diffs args))) | |
2045 | (magit-git-wash #'magit-diff-wash-diffs cmd args))) | |
2046 | ||
2047 | (defun magit-diff--maybe-add-stat-arguments (args) | |
2048 | (if (member "--stat" args) | |
2049 | (append (if (functionp magit-diff-extra-stat-arguments) | |
2050 | (funcall magit-diff-extra-stat-arguments) | |
2051 | magit-diff-extra-stat-arguments) | |
2052 | args) | |
2053 | args)) | |
2054 | ||
2055 | (defun magit-diff-use-window-width-as-stat-width () | |
2056 | "Use the `window-width' as the value of `--stat-width'." | |
2057 | (when-let ((window (get-buffer-window (current-buffer) 'visible))) | |
2058 | (list (format "--stat-width=%d" (window-width window))))) | |
2015 | 2059 | |
2016 | 2060 | (defun magit-diff-wash-diffs (args &optional limit) |
2061 | (run-hooks 'magit-diff-wash-diffs-hook) | |
2017 | 2062 | (when (member "--show-signature" args) |
2018 | 2063 | (magit-diff-wash-signature)) |
2019 | 2064 | (when (member "--stat" args) |
2117 | 2162 | 'font-lock-face 'magit-diff-file-heading)) |
2118 | 2163 | (insert ?\n)))) |
2119 | 2164 | t) |
2120 | ((looking-at (concat "^\\(merged\\|changed in both\\|" | |
2121 | "added in remote\\|removed in remote\\)")) | |
2122 | (let ((status (pcase (match-string 1) | |
2123 | ("merged" "merged") | |
2124 | ("changed in both" "conflict") | |
2125 | ("added in remote" "new file") | |
2126 | ("removed in remote" "deleted"))) | |
2127 | file orig base modes) | |
2165 | ((looking-at magit-diff-conflict-headline-re) | |
2166 | (let ((long-status (match-string 0)) | |
2167 | (status "BUG") | |
2168 | file orig base) | |
2169 | (if (equal long-status "merged") | |
2170 | (progn (setq status long-status) | |
2171 | (setq long-status nil)) | |
2172 | (setq status (pcase-exhaustive long-status | |
2173 | ("added in remote" "new file") | |
2174 | ("added in both" "new file") | |
2175 | ("added in local" "new file") | |
2176 | ("removed in both" "removed") | |
2177 | ("changed in both" "changed") | |
2178 | ("removed in local" "removed") | |
2179 | ("removed in remote" "removed")))) | |
2128 | 2180 | (magit-delete-line) |
2129 | 2181 | (while (looking-at |
2130 | 2182 | "^ \\([^ ]+\\) +[0-9]\\{6\\} \\([a-z0-9]\\{40\\}\\) \\(.+\\)$") |
2137 | 2189 | (magit-delete-line)) |
2138 | 2190 | (when orig (setq orig (magit-decode-git-path orig))) |
2139 | 2191 | (when file (setq file (magit-decode-git-path file))) |
2140 | (magit-diff-insert-file-section (or file base) orig status modes nil))) | |
2192 | (magit-diff-insert-file-section | |
2193 | (or file base) orig status nil nil long-status))) | |
2141 | 2194 | ((looking-at |
2142 | 2195 | "^diff --\\(?:\\(git\\) \\(?:\\(.+?\\) \\2\\)?\\|\\(cc\\|combined\\) \\(.+\\)\\)") |
2143 | 2196 | (let ((status (cond ((equal (match-string 1) "git") "modified") |
2181 | 2234 | (setq orig (substring orig 2)))) |
2182 | 2235 | (magit-diff-insert-file-section file orig status modes header))))) |
2183 | 2236 | |
2184 | (defun magit-diff-insert-file-section (file orig status modes header) | |
2237 | (defun magit-diff-insert-file-section | |
2238 | (file orig status modes header &optional long-status) | |
2185 | 2239 | (magit-insert-section section |
2186 | 2240 | (file file (or (equal status "deleted") |
2187 | 2241 | (derived-mode-p 'magit-status-mode))) |
2188 | (insert (propertize (format "%-10s %s\n" status | |
2242 | (insert (propertize (format "%-10s %s" status | |
2189 | 2243 | (if (or (not orig) (equal orig file)) |
2190 | 2244 | file |
2191 | 2245 | (format "%s -> %s" orig file))) |
2192 | 2246 | 'font-lock-face 'magit-diff-file-heading)) |
2247 | (when long-status | |
2248 | (insert (format " (%s)" long-status))) | |
2193 | 2249 | (magit-insert-heading) |
2194 | 2250 | (unless (equal orig file) |
2195 | 2251 | (oset section source orig)) |
2266 | 2322 | (when (looking-at "^@\\{2,\\} \\(.+?\\) @\\{2,\\}\\(?: \\(.*\\)\\)?") |
2267 | 2323 | (let* ((heading (match-string 0)) |
2268 | 2324 | (ranges (mapcar (lambda (str) |
2269 | (mapcar (lambda (n) (string-to-number n)) | |
2325 | (mapcar #'string-to-number | |
2270 | 2326 | (split-string (substring str 1) ","))) |
2271 | 2327 | (split-string (match-string 1)))) |
2272 | 2328 | (about (match-string 2)) |
2332 | 2388 | (magit-buffer-revision rev) |
2333 | 2389 | (magit-buffer-range (format "%s^..%s" rev rev)) |
2334 | 2390 | (magit-buffer-diff-args args) |
2335 | (magit-buffer-diff-files files))) | |
2391 | (magit-buffer-diff-files files) | |
2392 | (magit-buffer-diff-files-suspended nil))) | |
2336 | 2393 | |
2337 | 2394 | (defun magit-revision-refresh-buffer () |
2395 | (setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision)) | |
2338 | 2396 | (magit-set-header-line-format |
2339 | (concat (capitalize (magit-object-type magit-buffer-revision)) | |
2397 | (concat (magit-object-type magit-buffer-revision-hash) | |
2340 | 2398 | " " magit-buffer-revision |
2341 | 2399 | (pcase (length magit-buffer-diff-files) |
2342 | 2400 | (0) |
2343 | 2401 | (1 (concat " limited to file " (car magit-buffer-diff-files))) |
2344 | 2402 | (_ (concat " limited to files " |
2345 | 2403 | (mapconcat #'identity magit-buffer-diff-files ", ")))))) |
2346 | (setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision)) | |
2347 | 2404 | (magit-insert-section (commitbuf) |
2348 | 2405 | (magit-run-section-hook 'magit-revision-sections-hook))) |
2349 | 2406 | |
2392 | 2449 | (re-search-forward "-----END PGP SIGNATURE-----") |
2393 | 2450 | (delete-region beg (point))) |
2394 | 2451 | (insert ?\n) |
2395 | (process-file magit-git-executable nil t nil | |
2396 | "verify-tag" magit-buffer-revision)) | |
2452 | (magit-process-file magit-git-executable nil t nil | |
2453 | "verify-tag" magit-buffer-revision)) | |
2397 | 2454 | (goto-char (point-max))) |
2398 | 2455 | (insert ?\n)))) |
2399 | 2456 | |
2532 | 2589 | (eq magit-revision-insert-related-refs 'all)) |
2533 | 2590 | (magit--insert-related-refs |
2534 | 2591 | magit-buffer-revision "--contains" "Contained" |
2535 | (eq magit-revision-insert-related-refs '(all mixed))) | |
2592 | (memq magit-revision-insert-related-refs '(all mixed))) | |
2536 | 2593 | (when-let ((follows (magit-get-current-tag magit-buffer-revision t))) |
2537 | 2594 | (let ((tag (car follows)) |
2538 | 2595 | (cnt (cadr follows))) |
2785 | 2842 | (if (memq type '(file module)) |
2786 | 2843 | (magit-diff-type parent) |
2787 | 2844 | type))) |
2788 | (`hunk (-> it | |
2789 | (oref parent) | |
2790 | (oref parent) | |
2791 | (oref type))))))) | |
2845 | (`hunk (thread-first it | |
2846 | (oref parent) | |
2847 | (oref parent) | |
2848 | (oref type))))))) | |
2792 | 2849 | ((derived-mode-p 'magit-log-mode) |
2793 | 2850 | (if (or (and (magit-section-match 'commit section) |
2794 | 2851 | (oref section children)) |
0 | 0 | ;;; magit-ediff.el --- Ediff extension for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
103 | 105 | (defvar magit-ediff-previous-winconf nil) |
104 | 106 | |
105 | 107 | ;;;###autoload (autoload 'magit-ediff "magit-ediff" nil) |
106 | (define-transient-command magit-ediff () | |
108 | (transient-define-prefix magit-ediff () | |
107 | 109 | "Show differences using the Ediff package." |
108 | 110 | :info-manual "(ediff)" |
109 | 111 | ["Ediff" |
0 | 0 | ;;; magit-extras.el --- additional functionality for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | ||
7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> | |
8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> | |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
6 | 11 | |
7 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
8 | 13 | ;; under the terms of the GNU General Public License as published by |
23 | 28 | |
24 | 29 | ;;; Code: |
25 | 30 | |
26 | (eval-when-compile | |
27 | (require 'subr-x)) | |
28 | ||
29 | 31 | (require 'magit) |
30 | 32 | |
33 | (declare-function change-log-insert-entries "add-log" (changelogs)) | |
34 | (declare-function diff-add-log-current-defuns "diff-mode" ()) | |
31 | 35 | (declare-function dired-read-shell-command "dired-aux" (prompt arg files)) |
36 | ;; For `magit-project-status'. | |
37 | (declare-function project-root "project" (project)) | |
38 | (declare-function vc-git-command "vc-git" (buffer okstatus file-or-list &rest flags)) | |
32 | 39 | |
33 | 40 | (defvar ido-exit) |
34 | 41 | (defvar ido-fallback) |
42 | (defvar project-prefix-map) | |
43 | (defvar project-switch-commands) | |
35 | 44 | |
36 | 45 | (defgroup magit-extras nil |
37 | 46 | "Additional functionality for Magit." |
131 | 140 | (exit-minibuffer)) |
132 | 141 | |
133 | 142 | ;;;###autoload |
143 | (defun magit-project-status () | |
144 | "Run `magit-status' in the current project's root." | |
145 | (interactive) | |
146 | (magit-status-setup-buffer (project-root (project-current t)))) | |
147 | ||
148 | (defvar magit-bind-magit-project-status t | |
149 | "Whether to bind \"m\" to `magit-project-status' in `project-prefix-map'. | |
150 | If so, then an entry is added to `project-switch-commands' as | |
151 | well. If you want to use another key, then you must set this | |
152 | to nil before loading Magit to prevent \"m\" from being bound.") | |
153 | ||
154 | (with-eval-after-load 'project | |
155 | ;; Only more recent versions of project.el have `project-prefix-map' and | |
156 | ;; `project-switch-commands', though project.el is available in Emacs 25. | |
157 | (when (and magit-bind-magit-project-status | |
158 | (boundp 'project-prefix-map) | |
159 | ;; Only modify if it hasn't already been modified. | |
160 | (equal project-switch-commands | |
161 | (eval (car (get 'project-switch-commands 'standard-value)) | |
162 | t))) | |
163 | (define-key project-prefix-map "m" #'magit-project-status) | |
164 | (add-to-list 'project-switch-commands '(magit-project-status "Magit") t))) | |
165 | ||
166 | ;;;###autoload | |
134 | 167 | (defun magit-dired-jump (&optional other-window) |
135 | 168 | "Visit file at point using Dired. |
136 | 169 | With a prefix argument, visit in another window. If there |
248 | 281 | (put 'magit-clean 'disabled t) |
249 | 282 | |
250 | 283 | ;;; ChangeLog |
284 | ||
285 | (defun magit-generate-changelog (&optional amending) | |
286 | "Insert ChangeLog entries into the current buffer. | |
287 | ||
288 | The entries are generated from the diff being committed. | |
289 | If prefix argument, AMENDING, is non-nil, include changes | |
290 | in HEAD as well as staged changes in the diff to check." | |
291 | (interactive "P") | |
292 | (unless (magit-commit-message-buffer) | |
293 | (user-error "No commit in progress")) | |
294 | (require 'diff-mode) ; `diff-add-log-current-defuns'. | |
295 | (require 'vc-git) ; `vc-git-diff'. | |
296 | (require 'add-log) ; `change-log-insert-entries'. | |
297 | (unless (and (fboundp 'change-log-insert-entries) | |
298 | (fboundp 'diff-add-log-current-defuns)) | |
299 | (user-error "`magit-generate-changelog' requires Emacs 27 or better")) | |
300 | (setq default-directory | |
301 | (if (and (file-regular-p "gitdir") | |
302 | (not (magit-git-true "rev-parse" "--is-inside-work-tree")) | |
303 | (magit-git-true "rev-parse" "--is-inside-git-dir")) | |
304 | (file-name-directory (magit-file-line "gitdir")) | |
305 | (magit-toplevel))) | |
306 | (let ((rev1 (if amending "HEAD^1" "HEAD")) | |
307 | (rev2 nil)) | |
308 | ;; Magit may have updated the files without notifying vc, but | |
309 | ;; `diff-add-log-current-defuns' relies on vc being up-to-date. | |
310 | (mapc #'vc-file-clearprops (magit-staged-files)) | |
311 | (change-log-insert-entries | |
312 | (with-temp-buffer | |
313 | (vc-git-command (current-buffer) 1 nil | |
314 | "diff-index" "--exit-code" "--patch" | |
315 | (and (magit-anything-staged-p) "--cached") | |
316 | rev1 "--") | |
317 | ;; `diff-find-source-location' consults these vars. | |
318 | (defvar diff-vc-revisions) | |
319 | (setq-local diff-vc-revisions (list rev1 rev2)) | |
320 | (setq-local diff-vc-backend 'Git) | |
321 | (diff-add-log-current-defuns))))) | |
251 | 322 | |
252 | 323 | ;;;###autoload |
253 | 324 | (defun magit-add-change-log-entry (&optional whoami file-name other-window) |
347 | 418 | |
348 | 419 | ;;; Reshelve |
349 | 420 | |
350 | ;;;###autoload | |
351 | (defun magit-reshelve-since (rev) | |
421 | (defcustom magit-reshelve-since-committer-only nil | |
422 | "Whether `magit-reshelve-since' changes only the committer dates. | |
423 | Otherwise the author dates are also changed." | |
424 | :package-version '(magit . "3.0.0") | |
425 | :group 'magit-commands | |
426 | :type 'boolean) | |
427 | ||
428 | ;;;###autoload | |
429 | (defun magit-reshelve-since (rev keyid) | |
352 | 430 | "Change the author and committer dates of the commits since REV. |
353 | 431 | |
354 | 432 | Ask the user for the first reachable commit whose dates should |
358 | 436 | on. |
359 | 437 | |
360 | 438 | This command is only intended for interactive use and should only |
361 | be used on highly rearranged and unpublished history." | |
362 | (interactive (list nil)) | |
363 | (cond | |
364 | ((not rev) | |
365 | (let ((backup (concat "refs/original/refs/heads/" | |
366 | (magit-get-current-branch)))) | |
439 | be used on highly rearranged and unpublished history. | |
440 | ||
441 | If KEYID is non-nil, then use that to sign all reshelved commits. | |
442 | Interactively use the value of the \"--gpg-sign\" option in the | |
443 | list returned by `magit-rebase-arguments'." | |
444 | (interactive (list nil | |
445 | (transient-arg-value "--gpg-sign=" | |
446 | (magit-rebase-arguments)))) | |
447 | (let* ((current (or (magit-get-current-branch) | |
448 | (user-error "Refusing to reshelve detached head"))) | |
449 | (backup (concat "refs/original/refs/heads/" current))) | |
450 | (cond | |
451 | ((not rev) | |
367 | 452 | (when (and (magit-ref-p backup) |
368 | 453 | (not (magit-y-or-n-p |
369 | "Backup ref %s already exists. Override? " backup))) | |
370 | (user-error "Abort"))) | |
371 | (magit-log-select 'magit-reshelve-since | |
372 | "Type %p on a commit to reshelve it and the commits above it,")) | |
373 | (t | |
374 | (cl-flet ((adjust (time offset) | |
375 | (format-time-string | |
376 | "%F %T %z" | |
377 | (+ (floor time) | |
378 | (* offset 60) | |
379 | (- (car (decode-time time))))))) | |
380 | (let* ((start (concat rev "^")) | |
381 | (range (concat start ".." (magit-get-current-branch))) | |
382 | (time-rev (adjust (float-time (string-to-number | |
383 | (magit-rev-format "%at" start))) | |
384 | 1)) | |
385 | (time-now (adjust (float-time) | |
386 | (- (string-to-number | |
387 | (magit-git-string "rev-list" "--count" | |
388 | range)))))) | |
389 | (push time-rev magit--reshelve-history) | |
390 | (let ((date (floor | |
391 | (float-time | |
392 | (date-to-time | |
393 | (read-string "Date for first commit: " | |
394 | time-now 'magit--reshelve-history)))))) | |
395 | (magit-with-toplevel | |
396 | (magit-run-git-async | |
397 | "filter-branch" "--force" "--env-filter" | |
398 | (format "case $GIT_COMMIT in %s\nesac" | |
399 | (mapconcat (lambda (rev) | |
400 | (prog1 (format "%s) \ | |
401 | export GIT_AUTHOR_DATE=\"%s\"; \ | |
402 | export GIT_COMMITTER_DATE=\"%s\";;" rev date date) | |
403 | (cl-incf date 60))) | |
404 | (magit-git-lines "rev-list" "--reverse" | |
405 | range) | |
406 | " ")) | |
407 | range "--") | |
454 | (format "Backup ref %s already exists. Override? " backup)))) | |
455 | (user-error "Abort")) | |
456 | (magit-log-select | |
457 | (lambda (rev) | |
458 | (magit-reshelve-since rev keyid)) | |
459 | "Type %p on a commit to reshelve it and the commits above it,")) | |
460 | (t | |
461 | (cl-flet ((adjust (time offset) | |
462 | (format-time-string | |
463 | "%F %T %z" | |
464 | (+ (floor time) | |
465 | (* offset 60) | |
466 | (- (car (decode-time time))))))) | |
467 | (let* ((start (concat rev "^")) | |
468 | (range (concat start ".." current)) | |
469 | (time-rev (adjust (float-time (string-to-number | |
470 | (magit-rev-format "%at" start))) | |
471 | 1)) | |
472 | (time-now (adjust (float-time) | |
473 | (- (string-to-number | |
474 | (magit-git-string "rev-list" "--count" | |
475 | range)))))) | |
476 | (push time-rev magit--reshelve-history) | |
477 | (let ((date (floor | |
478 | (float-time | |
479 | (date-to-time | |
480 | (read-string "Date for first commit: " | |
481 | time-now 'magit--reshelve-history))))) | |
482 | (process-environment process-environment)) | |
483 | (push "FILTER_BRANCH_SQUELCH_WARNING=1" process-environment) | |
484 | (magit-with-toplevel | |
485 | (magit-run-git-async | |
486 | "filter-branch" "--force" "--env-filter" | |
487 | (format | |
488 | "case $GIT_COMMIT in %s\nesac" | |
489 | (mapconcat | |
490 | (lambda (rev) | |
491 | (prog1 (concat | |
492 | (format "%s) " rev) | |
493 | (and (not magit-reshelve-since-committer-only) | |
494 | (format "export GIT_AUTHOR_DATE=\"%s\"; " date)) | |
495 | (format "export GIT_COMMITTER_DATE=\"%s\";;" date)) | |
496 | (cl-incf date 60))) | |
497 | (magit-git-lines "rev-list" "--reverse" range) | |
498 | " ")) | |
499 | (and keyid | |
500 | (list "--commit-filter" | |
501 | (format "git commit-tree --gpg-sign=%s \"$@\";" | |
502 | keyid))) | |
503 | range "--")) | |
408 | 504 | (set-process-sentinel |
409 | 505 | magit-this-process |
410 | 506 | (lambda (process event) |
413 | 509 | (magit-process-sentinel process event) |
414 | 510 | (process-put process 'inhibit-refresh t) |
415 | 511 | (magit-process-sentinel process event) |
416 | (magit-run-git "update-ref" "-d" | |
417 | (concat "refs/original/refs/heads/" | |
418 | (magit-get-current-branch)))))))))))))) | |
512 | (magit-run-git "update-ref" "-d" backup)))))))))))) | |
419 | 513 | |
420 | 514 | ;;; Revision Stack |
421 | 515 | |
553 | 647 | (kbd "C-c C-w") 'magit-pop-revision-stack) |
554 | 648 | |
555 | 649 | ;;;###autoload |
556 | (defun magit-copy-section-value () | |
650 | (defun magit-copy-section-value (arg) | |
557 | 651 | "Save the value of the current section for later use. |
558 | 652 | |
559 | 653 | Save the section value to the `kill-ring', and, provided that |
571 | 665 | |
572 | 666 | When the region is active, then save that to the `kill-ring', |
573 | 667 | like `kill-ring-save' would, instead of behaving as described |
574 | above. If a prefix argument is used and the region is within a | |
575 | hunk, strip the outer diff marker column." | |
576 | (interactive) | |
668 | above. If a prefix argument is used and the region is within | |
669 | a hunk, then strip the diff marker column and keep only either | |
670 | the added or removed lines, depending on the sign of the prefix | |
671 | argument." | |
672 | (interactive "P") | |
577 | 673 | (cond |
578 | ((and current-prefix-arg | |
674 | ((and arg | |
579 | 675 | (magit-section-internal-region-p) |
580 | 676 | (magit-section-match 'hunk)) |
581 | (deactivate-mark) | |
582 | (kill-new (replace-regexp-in-string | |
583 | "^[ \\+\\-]" "" | |
584 | (buffer-substring-no-properties | |
585 | (region-beginning) (region-end))))) | |
677 | (kill-new | |
678 | (thread-last (buffer-substring-no-properties | |
679 | (region-beginning) | |
680 | (region-end)) | |
681 | (replace-regexp-in-string | |
682 | (format "^\\%c.*\n?" (if (< (prefix-numeric-value arg) 0) ?+ ?-)) | |
683 | "") | |
684 | (replace-regexp-in-string "^[ \\+\\-]" ""))) | |
685 | (deactivate-mark)) | |
586 | 686 | ((use-region-p) |
587 | 687 | (call-interactively #'copy-region-as-kill)) |
588 | 688 | (t |
0 | 0 | ;;; magit-fetch.el --- download objects and refs -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
40 | 42 | ;;; Commands |
41 | 43 | |
42 | 44 | ;;;###autoload (autoload 'magit-fetch "magit-fetch" nil t) |
43 | (define-transient-command magit-fetch () | |
45 | (transient-define-prefix magit-fetch () | |
44 | 46 | "Fetch from another repository." |
45 | 47 | :man-page "git-fetch" |
46 | 48 | ["Arguments" |
47 | 49 | ("-p" "Prune deleted branches" ("-p" "--prune")) |
48 | ("-t" "Fetch all tags" ("-t" "--tags"))] | |
50 | ("-t" "Fetch all tags" ("-t" "--tags")) | |
51 | (7 "-u" "Fetch full history" "--unshallow")] | |
49 | 52 | ["Fetch from" |
50 | 53 | ("p" magit-fetch-from-pushremote) |
51 | 54 | ("u" magit-fetch-from-upstream) |
66 | 69 | (magit-run-git-async "fetch" remote args)) |
67 | 70 | |
68 | 71 | ;;;###autoload (autoload 'magit-fetch-from-pushremote "magit-fetch" nil t) |
69 | (define-suffix-command magit-fetch-from-pushremote (args) | |
72 | (transient-define-suffix magit-fetch-from-pushremote (args) | |
70 | 73 | "Fetch from the current push-remote. |
71 | 74 | |
72 | 75 | With a prefix argument or when the push-remote is either not |
95 | 98 | (format "%s, setting that" v))))) |
96 | 99 | |
97 | 100 | ;;;###autoload (autoload 'magit-fetch-from-upstream "magit-fetch" nil t) |
98 | (define-suffix-command magit-fetch-from-upstream (remote args) | |
101 | (transient-define-suffix magit-fetch-from-upstream (remote args) | |
99 | 102 | "Fetch from the \"current\" remote, usually the upstream. |
100 | 103 | |
101 | 104 | If the upstream is configured for the current branch and names |
0 | 0 | ;;; magit-files.el --- finding files -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
28 | 30 | ;; modes. |
29 | 31 | |
30 | 32 | ;;; Code: |
31 | ||
32 | (eval-when-compile | |
33 | (require 'subr-x)) | |
34 | 33 | |
35 | 34 | (require 'magit) |
36 | 35 | |
205 | 204 | (unless (equal magit-buffer-refname "{index}") |
206 | 205 | (user-error "%s isn't visiting the index" file)) |
207 | 206 | (if (y-or-n-p (format "Update index with contents of %s" (buffer-name))) |
208 | (let ((index (make-temp-file "index")) | |
207 | (let ((index (make-temp-name (magit-git-dir "magit-update-index-"))) | |
209 | 208 | (buffer (current-buffer))) |
210 | 209 | (when magit-wip-before-change-mode |
211 | 210 | (magit-wip-commit-before-change (list file) " before un-/stage")) |
212 | (let ((coding-system-for-write buffer-file-coding-system)) | |
213 | (with-temp-file index | |
214 | (insert-buffer-substring buffer))) | |
215 | (magit-with-toplevel | |
216 | (magit-call-git "update-index" "--cacheinfo" | |
217 | (substring (magit-git-string "ls-files" "-s" file) | |
218 | 0 6) | |
219 | (magit-git-string "hash-object" "-t" "blob" "-w" | |
220 | (concat "--path=" file) | |
221 | "--" index) | |
222 | file)) | |
211 | (unwind-protect | |
212 | (progn | |
213 | (let ((coding-system-for-write buffer-file-coding-system)) | |
214 | (with-temp-file index | |
215 | (insert-buffer-substring buffer))) | |
216 | (magit-with-toplevel | |
217 | (magit-call-git | |
218 | "update-index" "--cacheinfo" | |
219 | (substring (magit-git-string "ls-files" "-s" file) | |
220 | 0 6) | |
221 | (magit-git-string "hash-object" "-t" "blob" "-w" | |
222 | (concat "--path=" file) | |
223 | "--" (magit-convert-filename-for-git index)) | |
224 | file))) | |
225 | (ignore-errors (delete-file index))) | |
223 | 226 | (set-buffer-modified-p nil) |
224 | 227 | (when magit-wip-after-apply-mode |
225 | 228 | (magit-wip-commit-after-apply (list file) " after un-/stage"))) |
278 | 281 | (confirm-nonexistent-file-or-buffer)))) |
279 | 282 | (find-file-other-frame filename wildcards)) |
280 | 283 | |
281 | ;;; File Mode | |
282 | ||
283 | (defvar magit-file-mode-map | |
284 | (let ((map (make-sparse-keymap))) | |
285 | (define-key map "\C-xg" 'magit-status) | |
286 | (define-key map "\C-x\M-g" 'magit-dispatch) | |
287 | (define-key map "\C-c\M-g" 'magit-file-dispatch) | |
288 | map) | |
289 | "Keymap for `magit-file-mode'.") | |
284 | ;;; File Dispatch | |
290 | 285 | |
291 | 286 | ;;;###autoload (autoload 'magit-file-dispatch "magit" nil t) |
292 | (define-transient-command magit-file-dispatch () | |
293 | "Invoke a Magit command that acts on the visited file." | |
287 | (transient-define-prefix magit-file-dispatch () | |
288 | "Invoke a Magit command that acts on the visited file. | |
289 | When invoked outside a file-visiting buffer, then fall back | |
290 | to `magit-dispatch'." | |
294 | 291 | :info-manual "(magit) Minor Mode for Buffers Visiting Files" |
295 | 292 | ["Actions" |
296 | 293 | [("s" "Stage" magit-stage-file) |
316 | 313 | [(5 "C-c r" "Rename file" magit-file-rename) |
317 | 314 | (5 "C-c d" "Delete file" magit-file-delete) |
318 | 315 | (5 "C-c u" "Untrack file" magit-file-untrack) |
319 | (5 "C-c c" "Checkout file" magit-file-checkout)]]) | |
320 | ||
321 | (defvar magit-file-mode-lighter "") | |
322 | ||
323 | (define-minor-mode magit-file-mode | |
324 | "Enable some Magit features in a file-visiting buffer. | |
325 | ||
326 | Currently this only adds the following key bindings. | |
327 | \n\\{magit-file-mode-map}" | |
328 | :package-version '(magit . "2.2.0") | |
329 | :lighter magit-file-mode-lighter | |
330 | :keymap magit-file-mode-map) | |
331 | ||
332 | (defun magit-file-mode-turn-on () | |
333 | (and buffer-file-name | |
334 | (magit-inside-worktree-p t) | |
335 | (magit-file-mode))) | |
336 | ||
337 | ;;;###autoload | |
338 | (define-globalized-minor-mode global-magit-file-mode | |
339 | magit-file-mode magit-file-mode-turn-on | |
340 | :package-version '(magit . "2.13.0") | |
341 | :link '(info-link "(magit)Minor Mode for Buffers Visiting Files") | |
342 | :group 'magit-essentials | |
343 | :group 'magit-modes | |
344 | :init-value t) | |
345 | ;; Unfortunately `:init-value t' only sets the value of the mode | |
346 | ;; variable but does not cause the mode function to be called, and we | |
347 | ;; cannot use `:initialize' to call that explicitly because the option | |
348 | ;; is defined before the functions, so we have to do it here. | |
349 | (cl-eval-when (load eval) | |
350 | (when global-magit-file-mode | |
351 | (global-magit-file-mode 1))) | |
316 | (5 "C-c c" "Checkout file" magit-file-checkout)]] | |
317 | (interactive) | |
318 | (transient-setup | |
319 | (if (magit-file-relative-name) | |
320 | 'magit-file-dispatch | |
321 | 'magit-dispatch))) | |
352 | 322 | |
353 | 323 | ;;; Blob Mode |
354 | 324 | |
429 | 399 | ;;; File Commands |
430 | 400 | |
431 | 401 | (defun magit-file-rename (file newname) |
432 | "Rename the FILE to NEWNAME. | |
433 | If FILE isn't tracked in Git, fallback to using `rename-file'." | |
402 | "Rename or move FILE to NEWNAME. | |
403 | NEWNAME may be a file or directory name. If FILE isn't tracked in | |
404 | Git, fallback to using `rename-file'." | |
434 | 405 | (interactive |
435 | 406 | (let* ((file (magit-read-file "Rename file")) |
436 | 407 | (dir (file-name-directory file)) |
437 | (newname (read-file-name (format "Rename %s to file: " file) | |
408 | (newname (read-file-name (format "Move %s to destination: " file) | |
438 | 409 | (and dir (expand-file-name dir))))) |
439 | 410 | (list (expand-file-name file (magit-toplevel)) |
440 | 411 | (expand-file-name newname)))) |
441 | (let ((oldbuf (get-file-buffer file))) | |
412 | (let ((oldbuf (get-file-buffer file)) | |
413 | (dstdir (file-name-directory newname)) | |
414 | (dstfile (if (directory-name-p newname) | |
415 | (concat newname (file-name-nondirectory file)) | |
416 | newname))) | |
442 | 417 | (when (and oldbuf (buffer-modified-p oldbuf)) |
443 | 418 | (user-error "Save %s before moving it" file)) |
444 | (when (file-exists-p newname) | |
445 | (user-error "%s already exists" newname)) | |
419 | (when (file-exists-p dstfile) | |
420 | (user-error "%s already exists" dstfile)) | |
421 | (unless (file-exists-p dstdir) | |
422 | (user-error "Destination directory %s does not exist" dstdir)) | |
446 | 423 | (if (magit-file-tracked-p (magit-convert-filename-for-git file)) |
447 | 424 | (magit-call-git "mv" |
448 | 425 | (magit-convert-filename-for-git file) |
451 | 428 | (when oldbuf |
452 | 429 | (with-current-buffer oldbuf |
453 | 430 | (let ((buffer-read-only buffer-read-only)) |
454 | (set-visited-file-name newname nil t)) | |
431 | (set-visited-file-name dstfile nil t)) | |
455 | 432 | (if (fboundp 'vc-refresh-state) |
456 | 433 | (vc-refresh-state) |
457 | 434 | (with-no-warnings |
0 | 0 | ;;; magit-git.el --- Git functionality -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
25 | 27 | ;; This library implements wrappers for various Git plumbing commands. |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | ||
29 | (require 'cl-lib) | |
30 | (require 'dash) | |
31 | ||
32 | (eval-when-compile | |
33 | (require 'subr-x)) | |
34 | 30 | |
35 | 31 | (require 'magit-utils) |
36 | 32 | (require 'magit-section) |
132 | 128 | (defcustom magit-git-executable |
133 | 129 | ;; Git might be installed in a different location on a remote, so |
134 | 130 | ;; it is better not to use the full path to the executable, except |
135 | ;; on Window were we would otherwise end up using one one of the | |
131 | ;; on Window where we would otherwise end up using one of the | |
136 | 132 | ;; wrappers "cmd/git.exe" or "cmd/git.cmd", which are much slower |
137 | 133 | ;; than using "bin/git.exe" directly. |
138 | 134 | (or (and (eq system-type 'windows-nt) |
296 | 292 | (with-editor* magit-with-editor-envvar |
297 | 293 | ,@body))) |
298 | 294 | |
295 | (defmacro magit--with-temp-process-buffer (&rest body) | |
296 | "Like `with-temp-buffer', but always propagate `process-environment'. | |
297 | When that var is buffer-local in the calling buffer, it is not | |
298 | propagated by `with-temp-buffer', so we explicitly ensure that | |
299 | happens, so that processes will be invoked consistently. BODY is | |
300 | as for that macro." | |
301 | (declare (indent 0) (debug (body))) | |
302 | (let ((p (cl-gensym))) | |
303 | `(let ((,p process-environment)) | |
304 | (with-temp-buffer | |
305 | (setq-local process-environment ,p) | |
306 | ,@body)))) | |
307 | ||
299 | 308 | (defun magit-process-git-arguments (args) |
300 | 309 | "Prepare ARGS for a function that invokes Git. |
301 | 310 | |
338 | 347 | This is an experimental replacement for `magit-git-string', and |
339 | 348 | still subject to major changes." |
340 | 349 | (magit--with-refresh-cache (cons default-directory args) |
341 | (with-temp-buffer | |
350 | (magit--with-temp-process-buffer | |
342 | 351 | (and (zerop (apply #'magit-process-file magit-git-executable nil t nil |
343 | 352 | (magit-process-git-arguments args))) |
344 | 353 | (not (bobp)) |
358 | 367 | still subject to major changes. Also see `magit-git-string-p'." |
359 | 368 | (magit--with-refresh-cache |
360 | 369 | (list default-directory 'magit-git-string-ng args) |
361 | (with-temp-buffer | |
370 | (magit--with-temp-process-buffer | |
362 | 371 | (let* ((args (magit-process-git-arguments args)) |
363 | 372 | (status (apply #'magit-process-file magit-git-executable |
364 | 373 | nil t nil args))) |
386 | 395 | ignore `magit-git-debug'." |
387 | 396 | (setq args (-flatten args)) |
388 | 397 | (magit--with-refresh-cache (cons default-directory args) |
389 | (with-temp-buffer | |
398 | (magit--with-temp-process-buffer | |
390 | 399 | (apply #'magit-process-file magit-git-executable nil (list t nil) nil |
391 | 400 | (magit-process-git-arguments args)) |
392 | 401 | (unless (bobp) |
397 | 406 | "Execute Git with ARGS, returning its output." |
398 | 407 | (setq args (-flatten args)) |
399 | 408 | (magit--with-refresh-cache (cons default-directory args) |
400 | (with-temp-buffer | |
409 | (magit--with-temp-process-buffer | |
401 | 410 | (apply #'magit-process-file magit-git-executable nil (list t nil) nil |
402 | 411 | (magit-process-git-arguments args)) |
403 | 412 | (buffer-substring-no-properties (point-min) (point-max))))) |
467 | 476 | newline, return an empty string." |
468 | 477 | (setq args (-flatten args)) |
469 | 478 | (magit--with-refresh-cache (cons default-directory args) |
470 | (with-temp-buffer | |
479 | (magit--with-temp-process-buffer | |
471 | 480 | (apply #'magit-git-insert args) |
472 | 481 | (unless (bobp) |
473 | 482 | (goto-char (point-min)) |
479 | 488 | |
480 | 489 | If Git exits with a non-zero exit status, then report show a |
481 | 490 | message and add a section in the respective process buffer." |
482 | (with-temp-buffer | |
491 | (magit--with-temp-process-buffer | |
483 | 492 | (apply #'magit-git-insert args) |
484 | 493 | (split-string (buffer-string) "\n" t))) |
485 | 494 | |
489 | 498 | |
490 | 499 | If Git exits with a non-zero exit status, then report show a |
491 | 500 | message and add a section in the respective process buffer." |
492 | (with-temp-buffer | |
501 | (magit--with-temp-process-buffer | |
493 | 502 | (apply #'magit-git-insert args) |
494 | 503 | (split-string (buffer-string) "\0" t))) |
495 | 504 | |
718 | 727 | ;; Git bug. See #2364. |
719 | 728 | (not (equal wtree ".git"))) |
720 | 729 | ;; Return the linked working tree. |
721 | (file-name-directory wtree)) | |
730 | (concat (file-remote-p default-directory) | |
731 | (file-name-directory wtree))) | |
722 | 732 | ;; The working directory may not be the parent directory of |
723 | 733 | ;; .git if it was set up with `git init --separate-git-dir'. |
724 | 734 | ;; See #2955. |
737 | 747 | (magit--not-inside-repository-error))))) |
738 | 748 | |
739 | 749 | (define-error 'magit-outside-git-repo "Not inside Git repository") |
750 | (define-error 'magit-corrupt-git-config "Corrupt Git configuration") | |
740 | 751 | (define-error 'magit-git-executable-not-found |
741 | 752 | "Git executable cannot be found (see https://magit.vc/goto/e6a78ed2)") |
742 | 753 | |
754 | (defun magit--assert-usable-git () | |
755 | (if (not (executable-find magit-git-executable)) | |
756 | (signal 'magit-git-executable-not-found magit-git-executable) | |
757 | (let ((magit-git-debug | |
758 | (lambda (err) | |
759 | (signal 'magit-corrupt-git-config | |
760 | (format "%s: %s" default-directory err))))) | |
761 | ;; This should always succeed unless there's a corrupt config | |
762 | ;; (or at least a similarly severe failing state). Note that | |
763 | ;; git-config's --default is avoided because it's not available | |
764 | ;; until Git 2.18. | |
765 | (magit-git-string "config" "--get-color" "" "reset")) | |
766 | nil)) | |
767 | ||
743 | 768 | (defun magit--not-inside-repository-error () |
744 | (if (executable-find magit-git-executable) | |
745 | (signal 'magit-outside-git-repo default-directory) | |
746 | (signal 'magit-git-executable-not-found magit-git-executable))) | |
769 | (magit--assert-usable-git) | |
770 | (signal 'magit-outside-git-repo default-directory)) | |
747 | 771 | |
748 | 772 | (defun magit-inside-gitdir-p (&optional noerror) |
749 | 773 | "Return t if `default-directory' is below the repository directory. |
892 | 916 | revA revB)))) |
893 | 917 | |
894 | 918 | (defun magit-file-status (&rest args) |
895 | (with-temp-buffer | |
919 | (magit--with-temp-process-buffer | |
896 | 920 | (save-excursion (magit-git-insert "status" "-z" args)) |
897 | 921 | (let ((pos (point)) status) |
898 | 922 | (while (> (skip-chars-forward "[:print:]") 0) |
1371 | 1395 | then return nil. I.e. return the name of an existing local or |
1372 | 1396 | remote-tracking branch. The returned string is colorized |
1373 | 1397 | according to the branch type." |
1374 | (when-let ((branch (or branch (magit-get-current-branch))) | |
1375 | (upstream (magit-ref-abbrev (concat branch "@{upstream}")))) | |
1376 | (magit--propertize-face | |
1377 | upstream (if (equal (magit-get "branch" branch "remote") ".") | |
1378 | 'magit-branch-local | |
1379 | 'magit-branch-remote)))) | |
1398 | (magit--with-refresh-cache (list 'magit-get-upstream-branch branch) | |
1399 | (when-let ((branch (or branch (magit-get-current-branch))) | |
1400 | (upstream (magit-ref-abbrev (concat branch "@{upstream}")))) | |
1401 | (magit--propertize-face | |
1402 | upstream (if (equal (magit-get "branch" branch "remote") ".") | |
1403 | 'magit-branch-local | |
1404 | 'magit-branch-remote))))) | |
1380 | 1405 | |
1381 | 1406 | (defun magit-get-indirect-upstream-branch (branch &optional force) |
1382 | 1407 | (let ((remote (magit-get "branch" branch "remote"))) |
1429 | 1454 | (when-let ((remotes (magit-list-remotes)) |
1430 | 1455 | (remote (if (= (length remotes) 1) |
1431 | 1456 | (car remotes) |
1432 | (car (member "origin" remotes))))) | |
1457 | (magit-primary-remote)))) | |
1433 | 1458 | (magit--propertize-face remote 'magit-branch-remote)))) |
1434 | 1459 | |
1435 | 1460 | (defun magit-get-push-remote (&optional branch) |
1440 | 1465 | (magit--propertize-face remote 'magit-branch-remote))) |
1441 | 1466 | |
1442 | 1467 | (defun magit-get-push-branch (&optional branch verify) |
1443 | (when-let ((branch (or branch (setq branch (magit-get-current-branch)))) | |
1444 | (remote (magit-get-push-remote branch)) | |
1445 | (target (concat remote "/" branch))) | |
1446 | (and (or (not verify) | |
1447 | (magit-rev-verify target)) | |
1448 | (magit--propertize-face target 'magit-branch-remote)))) | |
1468 | (magit--with-refresh-cache (list 'magit-get-push-branch branch verify) | |
1469 | (when-let ((branch (or branch (setq branch (magit-get-current-branch)))) | |
1470 | (remote (magit-get-push-remote branch)) | |
1471 | (target (concat remote "/" branch))) | |
1472 | (and (or (not verify) | |
1473 | (magit-rev-verify target)) | |
1474 | (magit--propertize-face target 'magit-branch-remote))))) | |
1449 | 1475 | |
1450 | 1476 | (defun magit-get-@{push}-branch (&optional branch) |
1451 | 1477 | (let ((ref (magit-rev-parse "--symbolic-full-name" |
1461 | 1487 | |
1462 | 1488 | (defun magit-get-some-remote (&optional branch) |
1463 | 1489 | (or (magit-get-remote branch) |
1464 | (and (magit-branch-p "master") | |
1465 | (magit-get-remote "master")) | |
1466 | (let ((remotes (magit-list-remotes))) | |
1467 | (or (car (member "origin" remotes)) | |
1468 | (car remotes))))) | |
1490 | (when-let ((main (magit-main-branch))) | |
1491 | (magit-get-remote main)) | |
1492 | (magit-primary-remote) | |
1493 | (car (magit-list-remotes)))) | |
1494 | ||
1495 | (defvar magit-primary-remote-names | |
1496 | '("upstream" "origin")) | |
1497 | ||
1498 | (defun magit-primary-remote () | |
1499 | "Return the primary remote. | |
1500 | ||
1501 | The primary remote is the remote that tracks the repository that | |
1502 | other repositories are forked from. It often is called \"origin\" | |
1503 | but because many people name their own fork \"origin\", using that | |
1504 | term would be ambiguous. Likewise we avoid the term \"upstream\" | |
1505 | because a branch's @{upstream} branch may be a local branch or a | |
1506 | branch from a remote other than the primary remote. | |
1507 | ||
1508 | If a remote exists whose name matches `magit.primaryRemote', then | |
1509 | that is considered the primary remote. If no remote by that name | |
1510 | exists, then remotes in `magit-primary-remote-names' are tried in | |
1511 | order and the first remote from that list that actually exists in | |
1512 | the current repository is considered its primary remote." | |
1513 | (let ((remotes (magit-list-remotes))) | |
1514 | (seq-find (lambda (name) | |
1515 | (member name remotes)) | |
1516 | (delete-dups | |
1517 | (delq nil | |
1518 | (cons (magit-get "magit.primaryRemote") | |
1519 | magit-primary-remote-names)))))) | |
1469 | 1520 | |
1470 | 1521 | (defun magit-branch-merged-p (branch &optional target) |
1471 | 1522 | "Return non-nil if BRANCH is merged into its upstream and TARGET. |
1494 | 1545 | the name of a remote and REF is the ref local to the remote." |
1495 | 1546 | (when-let ((ref (magit-ref-fullname refname))) |
1496 | 1547 | (save-match-data |
1497 | (-some (lambda (line) | |
1498 | (and (string-match "\ | |
1548 | (seq-some (lambda (line) | |
1549 | (and (string-match "\ | |
1499 | 1550 | \\`remote\\.\\([^.]+\\)\\.fetch=\\+?\\([^:]+\\):\\(.+\\)" line) |
1500 | (let ((rmt (match-string 1 line)) | |
1501 | (src (match-string 2 line)) | |
1502 | (dst (match-string 3 line))) | |
1503 | (and (string-match (format "\\`%s\\'" | |
1504 | (replace-regexp-in-string | |
1505 | "*" "\\(.+\\)" dst t t)) | |
1506 | ref) | |
1507 | (cons rmt (replace-regexp-in-string | |
1508 | "*" (match-string 1 ref) src)))))) | |
1509 | (magit-git-lines "config" "--local" "--list"))))) | |
1551 | (let ((rmt (match-string 1 line)) | |
1552 | (src (match-string 2 line)) | |
1553 | (dst (match-string 3 line))) | |
1554 | (and (string-match (format "\\`%s\\'" | |
1555 | (replace-regexp-in-string | |
1556 | "*" "\\(.+\\)" dst t t)) | |
1557 | ref) | |
1558 | (cons rmt (replace-regexp-in-string | |
1559 | "*" (match-string 1 ref) src)))))) | |
1560 | (magit-git-lines "config" "--local" "--list"))))) | |
1510 | 1561 | |
1511 | 1562 | (defun magit-split-branch-name (branch) |
1512 | 1563 | (cond ((member branch (magit-list-local-branch-names)) |
1513 | 1564 | (cons "." branch)) |
1514 | 1565 | ((string-match "/" branch) |
1515 | (or (-some (lambda (remote) | |
1516 | (and (string-match (format "\\`\\(%s\\)/\\(.+\\)\\'" remote) | |
1517 | branch) | |
1518 | (cons (match-string 1 branch) | |
1519 | (match-string 2 branch)))) | |
1520 | (magit-list-remotes)) | |
1566 | (or (seq-some (lambda (remote) | |
1567 | (and (string-match | |
1568 | (format "\\`\\(%s\\)/\\(.+\\)\\'" remote) | |
1569 | branch) | |
1570 | (cons (match-string 1 branch) | |
1571 | (match-string 2 branch)))) | |
1572 | (magit-list-remotes)) | |
1521 | 1573 | (error "Invalid branch name %s" branch))))) |
1522 | 1574 | |
1523 | 1575 | (defun magit-get-current-tag (&optional rev with-distance) |
1602 | 1654 | (magit-list-related-branches "--contains" commit args)) |
1603 | 1655 | |
1604 | 1656 | (defun magit-list-publishing-branches (&optional commit) |
1605 | (--filter (magit-rev-ancestor-p commit it) | |
1657 | (--filter (magit-rev-ancestor-p (or commit "HEAD") it) | |
1606 | 1658 | magit-published-branches)) |
1607 | 1659 | |
1608 | 1660 | (defun magit-list-merged-branches (&optional commit &rest args) |
1695 | 1747 | (substring it 41)) |
1696 | 1748 | (magit-git-lines "ls-remote" remote))) |
1697 | 1749 | |
1750 | (defun magit-list-modified-modules () | |
1751 | (--keep (and (string-match "\\`\\+\\([^ ]+\\) \\(.+\\) (.+)\\'" it) | |
1752 | (match-string 2 it)) | |
1753 | (magit-git-lines "submodule" "status"))) | |
1754 | ||
1698 | 1755 | (defun magit-list-module-paths () |
1699 | 1756 | (--mapcat (and (string-match "^160000 [0-9a-z]\\{40\\} 0\t\\(.+\\)$" it) |
1700 | 1757 | (list (match-string 1 it))) |
1701 | 1758 | (magit-git-items "ls-files" "-z" "--stage"))) |
1702 | 1759 | |
1760 | (defun magit-list-module-names () | |
1761 | (mapcar #'magit-get-submodule-name (magit-list-module-paths))) | |
1762 | ||
1703 | 1763 | (defun magit-get-submodule-name (path) |
1704 | 1764 | "Return the name of the submodule at PATH. |
1705 | 1765 | PATH has to be relative to the super-repository." |
1706 | (cadr (split-string | |
1707 | (car (or (magit-git-items | |
1708 | "config" "-z" | |
1709 | "-f" (expand-file-name ".gitmodules" (magit-toplevel)) | |
1710 | "--get-regexp" "^submodule\\..*\\.path$" | |
1711 | (concat "^" (regexp-quote (directory-file-name path)) "$")) | |
1712 | (error "No such submodule `%s'" path))) | |
1713 | "\n"))) | |
1766 | (magit-git-string "submodule--helper" "name" path)) | |
1714 | 1767 | |
1715 | 1768 | (defun magit-list-worktrees () |
1716 | 1769 | (let (worktrees worktree) |
1767 | 1820 | (defun magit-remote-p (string) |
1768 | 1821 | (car (member string (magit-list-remotes)))) |
1769 | 1822 | |
1823 | (defvar magit-main-branch-names | |
1824 | ;; These are the names that Git suggests | |
1825 | ;; if `init.defaultBranch' is undefined. | |
1826 | '("main" "master" "trunk" "development")) | |
1827 | ||
1828 | (defun magit-main-branch () | |
1829 | "Return the main branch. | |
1830 | ||
1831 | If a branch exists whose name matches `init.defaultBranch', then | |
1832 | that is considered the main branch. If no branch by that name | |
1833 | exists, then the branch names in `magit-main-branch-names' are | |
1834 | tried in order. The first branch from that list that actually | |
1835 | exists in the current repository is considered its main branch." | |
1836 | (let ((branches (magit-list-local-branch-names))) | |
1837 | (seq-find (lambda (name) | |
1838 | (member name branches)) | |
1839 | (delete-dups | |
1840 | (delq nil | |
1841 | (cons (magit-get "init.defaultBranch") | |
1842 | magit-main-branch-names)))))) | |
1843 | ||
1770 | 1844 | (defun magit-rev-diff-count (a b) |
1771 | 1845 | "Return the commits in A but not B and vice versa. |
1772 | 1846 | Return a list of two integers: (A>B B>A)." |
1777 | 1851 | "\t"))) |
1778 | 1852 | |
1779 | 1853 | (defun magit-abbrev-length () |
1780 | (--if-let (magit-get "core.abbrev") | |
1781 | (string-to-number it) | |
1782 | ;; Guess the length git will be using based on an example | |
1783 | ;; abbreviation. Actually HEAD's abbreviation might be an | |
1784 | ;; outlier, so use the shorter of the abbreviations for two | |
1785 | ;; commits. When a commit does not exist, then fall back | |
1786 | ;; to the default of 7. See #3034. | |
1787 | (min (--if-let (magit-rev-parse "--short" "HEAD") (length it) 7) | |
1788 | (--if-let (magit-rev-parse "--short" "HEAD~") (length it) 7)))) | |
1854 | (let ((abbrev (magit-get "core.abbrev"))) | |
1855 | (if (and abbrev (not (equal abbrev "auto"))) | |
1856 | (string-to-number abbrev) | |
1857 | ;; Guess the length git will be using based on an example | |
1858 | ;; abbreviation. Actually HEAD's abbreviation might be an | |
1859 | ;; outlier, so use the shorter of the abbreviations for two | |
1860 | ;; commits. See #3034. | |
1861 | (if-let ((head (magit-rev-parse "--short" "HEAD")) | |
1862 | (head-len (length head))) | |
1863 | (min head-len | |
1864 | (--if-let (magit-rev-parse "--short" "HEAD~") | |
1865 | (length it) | |
1866 | head-len)) | |
1867 | ;; We're on an unborn branch, but perhaps the repository has | |
1868 | ;; other commits. See #4123. | |
1869 | (if-let ((commits (magit-git-lines "rev-list" "-n2" "--all" | |
1870 | "--abbrev-commit"))) | |
1871 | (apply #'min (mapcar #'length commits)) | |
1872 | ;; A commit does not exist. Fall back to the default of 7. | |
1873 | 7))))) | |
1789 | 1874 | |
1790 | 1875 | (defun magit-abbrev-arg (&optional arg) |
1791 | 1876 | (format "--%s=%d" (or arg "abbrev") (magit-abbrev-length))) |
1807 | 1892 | (cdr (split-string it)))) |
1808 | 1893 | |
1809 | 1894 | (defun magit-patch-id (rev) |
1810 | (with-temp-buffer | |
1895 | (magit--with-temp-process-buffer | |
1811 | 1896 | (magit-process-file |
1812 | 1897 | shell-file-name nil '(t nil) nil shell-command-switch |
1813 | 1898 | (let ((exec (shell-quote-argument magit-git-executable))) |
1958 | 2043 | (defmacro magit-with-blob (commit file &rest body) |
1959 | 2044 | (declare (indent 2) |
1960 | 2045 | (debug (form form body))) |
1961 | `(with-temp-buffer | |
2046 | `(magit--with-temp-process-buffer | |
1962 | 2047 | (let ((buffer-file-name ,file)) |
1963 | 2048 | (save-excursion |
1964 | 2049 | (magit-git-insert "cat-file" "-p" |
2053 | 2138 | (--when-let |
2054 | 2139 | (let ((c "\s\n\t~^:?*[\\")) |
2055 | 2140 | (cl-letf (((get 'git-revision 'beginning-op) |
2056 | (if (re-search-backward (format "[%s]" c) nil t) | |
2057 | (forward-char) | |
2058 | (goto-char (point-min)))) | |
2141 | (lambda () | |
2142 | (if (re-search-backward (format "[%s]" c) nil t) | |
2143 | (forward-char) | |
2144 | (goto-char (point-min))))) | |
2059 | 2145 | ((get 'git-revision 'end-op) |
2060 | 2146 | (lambda () |
2061 | 2147 | (re-search-forward (format "\\=[^%s]*" c) nil t)))) |
2062 | 2148 | (bounds-of-thing-at-point 'git-revision))) |
2063 | 2149 | (let ((text (buffer-substring-no-properties (car it) (cdr it)))) |
2064 | (and (magit-commit-p text) text)))) | |
2150 | (and (>= (length text) 7) | |
2151 | (string-match-p "[a-z]" text) | |
2152 | (magit-commit-p text) | |
2153 | text)))) | |
2065 | 2154 | |
2066 | 2155 | ;;; Completion |
2067 | 2156 | |
2217 | 2306 | (or (let ((r (car (member (magit-remote-branch-at-point) branches))) |
2218 | 2307 | (l (car (member (magit-local-branch-at-point) branches)))) |
2219 | 2308 | (if magit-prefer-remote-upstream (or r l) (or l r))) |
2220 | (let ((r (car (member "origin/master" branches))) | |
2221 | (l (car (member "master" branches)))) | |
2222 | (if magit-prefer-remote-upstream (or r l) (or l r))) | |
2309 | (when-let ((main (magit-main-branch))) | |
2310 | (let ((r (car (member (concat "origin/" main) branches))) | |
2311 | (l (car (member main branches)))) | |
2312 | (if magit-prefer-remote-upstream (or r l) (or l r)))) | |
2223 | 2313 | (car (member (magit-get-previous-branch) branches)))))) |
2224 | 2314 | |
2225 | 2315 | (defun magit-read-starting-point (prompt &optional branch default) |
2253 | 2343 | (magit-tag-at-point))) |
2254 | 2344 | |
2255 | 2345 | (defun magit-read-stash (prompt) |
2256 | (let ((stashes (magit-list-stashes))) | |
2257 | (magit-completing-read prompt stashes nil t nil nil | |
2258 | (magit-stash-at-point) | |
2259 | (car stashes)))) | |
2346 | (let* ((atpoint (magit-stash-at-point)) | |
2347 | (default (and atpoint | |
2348 | (concat atpoint (magit-rev-format " %s" atpoint)))) | |
2349 | (choices (mapcar (lambda (c) | |
2350 | (pcase-let ((`(,rev ,msg) (split-string c "\0"))) | |
2351 | (concat (propertize rev 'face 'magit-hash) | |
2352 | " " msg))) | |
2353 | (magit-list-stashes "%gd%x00%s"))) | |
2354 | (choice (magit-completing-read prompt choices | |
2355 | nil t nil nil | |
2356 | default | |
2357 | (car choices)))) | |
2358 | (and choice | |
2359 | (string-match "^\\([^ ]+\\) \\(.+\\)" choice) | |
2360 | (substring-no-properties (match-string 1 choice))))) | |
2260 | 2361 | |
2261 | 2362 | (defun magit-read-remote (prompt &optional default use-only) |
2262 | 2363 | (let ((remotes (magit-list-remotes))) |
0 | 0 | ;;; magit-gitignore.el --- intentionally untracked files -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | 30 | |
29 | (eval-when-compile | |
30 | (require 'subr-x)) | |
31 | ||
32 | 31 | (require 'magit) |
33 | 32 | |
34 | 33 | ;;; Transient |
35 | 34 | |
36 | 35 | ;;;###autoload (autoload 'magit-gitignore "magit-gitignore" nil t) |
37 | (define-transient-command magit-gitignore () | |
36 | (transient-define-prefix magit-gitignore () | |
38 | 37 | "Instruct Git to ignore a file or pattern." |
39 | 38 | :man-page "gitignore" |
40 | 39 | ["Gitignore" |
70 | 69 | |
71 | 70 | ;;;###autoload |
72 | 71 | (defun magit-gitignore-in-subdir (rule directory) |
73 | "Add the Git ignore RULE to a \".gitignore\" file. | |
74 | Prompted the user for a directory and add the rule to the | |
72 | "Add the Git ignore RULE to a \".gitignore\" file in DIRECTORY. | |
73 | Prompt the user for a directory and add the rule to the | |
75 | 74 | \".gitignore\" file in that directory. Since such files are |
76 | 75 | tracked, they are shared with other clones of the repository. |
77 | 76 | Also stage the file." |
80 | 79 | (magit-with-toplevel |
81 | 80 | (let ((file (expand-file-name ".gitignore" directory))) |
82 | 81 | (magit--gitignore rule file) |
83 | (magit-run-git "add" file)))) | |
82 | (magit-run-git "add" (magit-convert-filename-for-git file))))) | |
84 | 83 | |
85 | 84 | ;;;###autoload |
86 | 85 | (defun magit-gitignore-in-gitdir (rule) |
0 | 0 | ;;; magit-imenu.el --- Integrate Imenu in magit major modes -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Damien Cassou <damien@cassou.me> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
31 | 33 | ;; magit-imenu.el adds Imenu support to every major mode in Magit. |
32 | 34 | |
33 | 35 | ;;; Code: |
34 | ||
35 | (eval-when-compile | |
36 | (require 'subr-x)) | |
37 | 36 | |
38 | 37 | (require 'magit) |
39 | 38 | (require 'git-rebase) |
0 | 0 | ;;; magit-libgit.el --- Libgit functionality -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; Package-Requires: ((emacs "26.1") (magit "0") (libgit "0")) | |
11 | 9 | ;; Keywords: git tools vc |
12 | 10 | ;; Homepage: https://github.com/magit/magit |
11 | ||
12 | ;; Package-Requires: ((emacs "26.1") (magit "3.0.0") (libgit "0")) | |
13 | ;; Package-Version: 0 | |
14 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
13 | 15 | |
14 | 16 | ;; Magit is free software; you can redistribute it and/or modify it |
15 | 17 | ;; under the terms of the GNU General Public License as published by |
41 | 43 | ;;; Code: |
42 | 44 | |
43 | 45 | (require 'cl-lib) |
46 | (require 'dash) | |
47 | (require 'eieio) | |
48 | (require 'seq) | |
44 | 49 | (require 'subr-x) |
45 | 50 | |
46 | 51 | (require 'magit-git) |
52 | ||
47 | 53 | (require 'libgit) |
48 | 54 | |
49 | 55 | ;;; Utilities |
0 | 0 | ;;; magit-log.el --- inspect Git history -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
52 | 54 | (require 'crm) |
53 | 55 | (require 'which-func) |
54 | 56 | |
55 | (eval-when-compile | |
56 | (require 'subr-x)) | |
57 | ||
58 | 57 | ;;; Options |
59 | 58 | ;;;; Log Mode |
60 | 59 | |
61 | 60 | (defgroup magit-log nil |
62 | 61 | "Inspect and manipulate Git history." |
63 | 62 | :link '(info-link "(magit)Logging") |
63 | :group 'magit-commands | |
64 | 64 | :group 'magit-modes) |
65 | 65 | |
66 | 66 | (defcustom magit-log-mode-hook nil |
317 | 317 | (pcase-let ((`(,args ,files) |
318 | 318 | (magit-log--get-value 'magit-log-mode |
319 | 319 | magit-prefix-use-buffer-arguments))) |
320 | (unless (eq current-transient-command 'magit-dispatch) | |
320 | (unless (eq transient-current-command 'magit-dispatch) | |
321 | 321 | (when-let ((file (magit-file-relative-name))) |
322 | 322 | (setq files (list file)))) |
323 | 323 | (oset obj value (if files `(("--" ,@files) ,args) args)))) |
338 | 338 | |
339 | 339 | (defun magit-log-arguments (&optional mode) |
340 | 340 | "Return the current log arguments." |
341 | (if (memq current-transient-command '(magit-log magit-log-refresh)) | |
341 | (if (memq transient-current-command '(magit-log magit-log-refresh)) | |
342 | 342 | (pcase-let ((`(,args ,alist) |
343 | 343 | (-separate #'atom (transient-get-value)))) |
344 | 344 | (list args (cdr (assoc "--" alist)))) |
391 | 391 | ;;;; Prefix Commands |
392 | 392 | |
393 | 393 | ;;;###autoload (autoload 'magit-log "magit-log" nil t) |
394 | (define-transient-command magit-log () | |
394 | (transient-define-prefix magit-log () | |
395 | 395 | "Show a commit or reference log." |
396 | 396 | :man-page "git-log" |
397 | 397 | :class 'magit-log-prefix |
408 | 408 | (7 "=s" "Limit to commits since" "--since=" transient-read-date) |
409 | 409 | (7 "=u" "Limit to commits until" "--until=" transient-read-date) |
410 | 410 | (magit-log:--grep) |
411 | (7 "-I" "Invert search pattern" "--invert-grep") | |
411 | (7 "-i" "Search case-insensitive" ("-i" "--regexp-ignore-case")) | |
412 | (7 "-I" "Invert search pattern" "--invert-grep") | |
412 | 413 | (magit-log:-G) ;2 |
413 | 414 | (magit-log:-S) ;2 |
414 | 415 | (magit-log:-L) ;2 |
449 | 450 | ("r" "current" magit-reflog-current) |
450 | 451 | ("O" "other" magit-reflog-other) |
451 | 452 | ("H" "HEAD" magit-reflog-head)] |
452 | [:if magit--any-wip-mode-enabled-p | |
453 | [:if (lambda () | |
454 | (require 'magit-wip) | |
455 | (magit--any-wip-mode-enabled-p)) | |
453 | 456 | :description "Wiplog" |
454 | 457 | ("i" "index" magit-wip-log-index) |
455 | ("w" "worktree" magit-wip-log-worktree)]]) | |
458 | ("w" "worktree" magit-wip-log-worktree)] | |
459 | ["Other" | |
460 | (5 "s" "shortlog" magit-shortlog)]]) | |
456 | 461 | |
457 | 462 | ;;;###autoload (autoload 'magit-log-refresh "magit-log" nil t) |
458 | (define-transient-command magit-log-refresh () | |
463 | (transient-define-prefix magit-log-refresh () | |
459 | 464 | "Change the arguments used for the log(s) in the current buffer." |
460 | 465 | :man-page "git-log" |
461 | 466 | :class 'magit-log-refresh-prefix |
465 | 470 | (magit-log:-n) |
466 | 471 | (magit:--author) |
467 | 472 | (magit-log:--grep) |
468 | (7 "-I" "Invert search pattern" "--invert-grep") | |
473 | (7 "-i" "Search case-insensitive" ("-i" "--regexp-ignore-case")) | |
474 | (7 "-I" "Invert search pattern" "--invert-grep") | |
469 | 475 | (magit-log:-G) |
470 | 476 | (magit-log:-S) |
471 | 477 | (magit-log:-L)] |
510 | 516 | ("b" "buffer lock" magit-toggle-buffer-lock)]] |
511 | 517 | (interactive) |
512 | 518 | (cond |
513 | ((not (eq current-transient-command 'magit-log-refresh)) | |
519 | ((not (eq transient-current-command 'magit-log-refresh)) | |
514 | 520 | (pcase major-mode |
515 | 521 | (`magit-reflog-mode |
516 | 522 | (user-error "Cannot change log arguments in reflog buffers")) |
526 | 532 | |
527 | 533 | ;;;; Infix Commands |
528 | 534 | |
529 | (define-infix-argument magit-log:-n () | |
535 | (transient-define-argument magit-log:-n () | |
530 | 536 | :description "Limit number of commits" |
531 | 537 | :class 'transient-option |
532 | 538 | ;; For historic reasons (and because it easy to guess what "-n" |
536 | 542 | :argument "-n" |
537 | 543 | :reader 'transient-read-number-N+) |
538 | 544 | |
539 | (define-infix-argument magit:--author () | |
545 | (transient-define-argument magit:--author () | |
540 | 546 | :description "Limit to author" |
541 | 547 | :class 'transient-option |
542 | 548 | :key "-A" |
543 | 549 | :argument "--author=" |
544 | 550 | :reader 'magit-transient-read-person) |
545 | 551 | |
546 | (define-infix-argument magit-log:--*-order () | |
552 | (transient-define-argument magit-log:--*-order () | |
547 | 553 | :description "Order commits by" |
548 | 554 | :class 'transient-switches |
549 | 555 | :key "-o" |
551 | 557 | :argument-regexp "\\(--\\(topo\\|author-date\\|date\\)-order\\)" |
552 | 558 | :choices '("topo" "author-date" "date")) |
553 | 559 | |
554 | (define-infix-argument magit-log:--grep () | |
560 | (transient-define-argument magit-log:--grep () | |
555 | 561 | :description "Search messages" |
556 | 562 | :class 'transient-option |
557 | 563 | :key "-F" |
558 | 564 | :argument "--grep=") |
559 | 565 | |
560 | (define-infix-argument magit-log:-G () | |
566 | (transient-define-argument magit-log:-G () | |
561 | 567 | :description "Search changes" |
562 | 568 | :class 'transient-option |
563 | 569 | :argument "-G") |
564 | 570 | |
565 | (define-infix-argument magit-log:-S () | |
571 | (transient-define-argument magit-log:-S () | |
566 | 572 | :description "Search occurrences" |
567 | 573 | :class 'transient-option |
568 | 574 | :argument "-S") |
569 | 575 | |
570 | (define-infix-argument magit-log:-L () | |
576 | (transient-define-argument magit-log:-L () | |
571 | 577 | :description "Trace line evolution" |
572 | 578 | :class 'transient-option |
573 | 579 | :argument "-L" |
870 | 876 | "\\[magit-log-double-commit-limit] first")))) |
871 | 877 | (user-error "Parent %s does not exist" parent-rev)))))) |
872 | 878 | |
879 | ;;;; Shortlog Commands | |
880 | ||
881 | ;;;###autoload (autoload 'magit-shortlog "magit-log" nil t) | |
882 | (transient-define-prefix magit-shortlog () | |
883 | "Show a history summary." | |
884 | :man-page "git-shortlog" | |
885 | :value '("--numbered" "--summary") | |
886 | ["Arguments" | |
887 | ("-n" "Sort by number of commits" ("-n" "--numbered")) | |
888 | ("-s" "Show commit count summary only" ("-s" "--summary")) | |
889 | ("-e" "Show email addresses" ("-e" "--email")) | |
890 | ("-g" "Group commits by" "--group=" | |
891 | :choices ("author" "committer" "trailer:")) | |
892 | (7 "-f" "Format string" "--format=") | |
893 | (7 "-w" "Linewrap" "-w" :class transient-option)] | |
894 | ["Shortlog" | |
895 | ("s" "since" magit-shortlog-since) | |
896 | ("r" "range" magit-shortlog-range)]) | |
897 | ||
898 | (defun magit-git-shortlog (rev args) | |
899 | (with-current-buffer (get-buffer-create "*magit-shortlog*") | |
900 | (erase-buffer) | |
901 | (save-excursion | |
902 | (magit-git-insert "shortlog" args rev)) | |
903 | (switch-to-buffer-other-window (current-buffer)))) | |
904 | ||
905 | ;;;###autoload | |
906 | (defun magit-shortlog-since (rev args) | |
907 | "Show a history summary for commits since REV." | |
908 | (interactive | |
909 | (list (magit-read-branch-or-commit "Shortlog since" (magit-get-current-tag)) | |
910 | (transient-args 'magit-shortlog))) | |
911 | (magit-git-shortlog (concat rev "..") args)) | |
912 | ||
913 | ;;;###autoload | |
914 | (defun magit-shortlog-range (rev-or-range args) | |
915 | "Show a history summary for commit or range REV-OR-RANGE." | |
916 | (interactive | |
917 | (list (magit-read-range-or-commit "Shortlog for revision or range") | |
918 | (transient-args 'magit-shortlog))) | |
919 | (magit-git-shortlog rev-or-range args)) | |
920 | ||
873 | 921 | ;;; Log Mode |
874 | 922 | |
875 | 923 | (defvar magit-log-disable-graph-hack-args |
998 | 1046 | (remove "--literal-pathspecs" magit-git-global-arguments))) |
999 | 1047 | (magit-git-wash (apply-partially #'magit-log-wash-log 'log) |
1000 | 1048 | "log" |
1001 | (format "--format=%s%%h%%x00%s%%x00%s%%x00%%aN%%x00%s%%x00%%s%s" | |
1049 | (format "--format=%s%%h%%x0c%s%%x0c%s%%x0c%%aN%%x0c%s%%x0c%%s%s" | |
1002 | 1050 | (if (and (member "--left-right" args) |
1003 | 1051 | (not (member "--graph" args))) |
1004 | 1052 | "%m " |
1021 | 1069 | (setq args (cons "--decorate=full" (remove "--decorate" args)))) |
1022 | 1070 | (when (member "--reverse" args) |
1023 | 1071 | (setq args (remove "--graph" args))) |
1072 | (setq args (magit-diff--maybe-add-stat-arguments args)) | |
1024 | 1073 | args) |
1025 | 1074 | "--use-mailmap" "--no-prefix" revs "--" files))) |
1026 | 1075 | |
1038 | 1087 | "Keymap for `module-commit' sections.") |
1039 | 1088 | |
1040 | 1089 | (defconst magit-log-heading-re |
1090 | ;; Note: A form feed instead of a null byte is used as the delimiter | |
1091 | ;; because using the latter interferes with the graph prefix when | |
1092 | ;; ++header is used. | |
1041 | 1093 | (concat "^" |
1042 | 1094 | "\\(?4:[-_/|\\*o<>. ]*\\)" ; graph |
1043 | "\\(?1:[0-9a-fA-F]+\\)?\0" ; sha1 | |
1044 | "\\(?3:[^\0\n]+\\)?\0" ; refs | |
1045 | "\\(?7:[BGUXYREN]\\)?\0" ; gpg | |
1046 | "\\(?5:[^\0\n]*\\)\0" ; author | |
1095 | "\\(?1:[0-9a-fA-F]+\\)?" ; sha1 | |
1096 | "\\(?3:[^\n]+\\)?" ; refs | |
1097 | "\\(?7:[BGUXYREN]\\)?" ; gpg | |
1098 | "\\(?5:[^\n]*\\)" ; author | |
1047 | 1099 | ;; Note: Date is optional because, prior to Git v2.19.0, |
1048 | 1100 | ;; `git rebase -i --root` corrupts the root's author date. |
1049 | "\\(?6:[^\0\n]*\\)\0" ; date | |
1101 | "\\(?6:[^\n]*\\)" ; date | |
1050 | 1102 | "\\(?2:.*\\)$")) ; msg |
1051 | 1103 | |
1052 | 1104 | (defconst magit-log-cherry-re |
1070 | 1122 | |
1071 | 1123 | (defconst magit-log-bisect-log-re |
1072 | 1124 | (concat "^# " |
1073 | "\\(?3:bad:\\|skip:\\|good:\\) " ; "refs" | |
1125 | "\\(?3:[^: \n]+:\\) " ; "refs" | |
1074 | 1126 | "\\[\\(?1:[^]\n]+\\)\\] " ; sha1 |
1075 | 1127 | "\\(?2:.*\\)$")) ; msg |
1076 | 1128 | |
1160 | 1212 | (`stash (oset section type 'stash)) |
1161 | 1213 | (`module (oset section type 'module-commit)) |
1162 | 1214 | (`bisect-log (setq hash (magit-rev-parse "--short" hash)))) |
1215 | (setq hash (propertize hash 'font-lock-face | |
1216 | (pcase (and gpg (aref gpg 0)) | |
1217 | (?G 'magit-signature-good) | |
1218 | (?B 'magit-signature-bad) | |
1219 | (?U 'magit-signature-untrusted) | |
1220 | (?X 'magit-signature-expired) | |
1221 | (?Y 'magit-signature-expired-key) | |
1222 | (?R 'magit-signature-revoked) | |
1223 | (?E 'magit-signature-error) | |
1224 | (?N 'magit-hash) | |
1225 | (_ 'magit-hash)))) | |
1163 | 1226 | (when cherry |
1164 | 1227 | (when (and (derived-mode-p 'magit-refs-mode) |
1165 | 1228 | magit-refs-show-commit-count) |
1176 | 1239 | 'magit-cherry-unmatched))) |
1177 | 1240 | (insert ?\s)) |
1178 | 1241 | (when align |
1179 | (insert (propertize hash 'font-lock-face 'magit-hash) ?\s)) | |
1242 | (insert hash ?\s)) | |
1180 | 1243 | (when graph |
1181 | 1244 | (insert graph)) |
1182 | 1245 | (unless align |
1183 | (insert (propertize hash 'font-lock-face 'magit-hash) ?\s)) | |
1246 | (insert hash ?\s)) | |
1184 | 1247 | (when (and refs (not magit-log-show-refname-after-summary)) |
1185 | 1248 | (insert (magit-format-ref-labels refs) ?\s)) |
1186 | 1249 | (when (eq style 'reflog) |
1189 | 1252 | (insert (magit-reflog-format-subject |
1190 | 1253 | (substring refsub 0 (if (string-match-p ":" refsub) -2 -1)))))) |
1191 | 1254 | (when msg |
1192 | (when gpg | |
1193 | (setq msg (propertize msg 'font-lock-face | |
1194 | (pcase (aref gpg 0) | |
1195 | (?G 'magit-signature-good) | |
1196 | (?B 'magit-signature-bad) | |
1197 | (?U 'magit-signature-untrusted) | |
1198 | (?X 'magit-signature-expired) | |
1199 | (?Y 'magit-signature-expired-key) | |
1200 | (?R 'magit-signature-revoked) | |
1201 | (?E 'magit-signature-error))))) | |
1202 | 1255 | (insert (funcall magit-log-format-message-function hash msg))) |
1203 | 1256 | (when (and refs magit-log-show-refname-after-summary) |
1204 | 1257 | (insert ?\s) |
1312 | 1365 | (setq magit--update-revision-buffer (list commit buffer)) |
1313 | 1366 | (run-with-idle-timer |
1314 | 1367 | magit-update-other-window-delay nil |
1315 | (let ((args (with-current-buffer buffer | |
1316 | (let ((magit-direct-use-buffer-arguments 'selected)) | |
1317 | (magit-show-commit--arguments))))) | |
1368 | (let ((args (let ((magit-direct-use-buffer-arguments 'selected)) | |
1369 | (magit-show-commit--arguments)))) | |
1318 | 1370 | (lambda () |
1319 | 1371 | (pcase-let ((`(,rev ,buf) magit--update-revision-buffer)) |
1320 | 1372 | (setq magit--update-revision-buffer nil) |
1394 | 1446 | (truncate-string-to-width |
1395 | 1447 | (or author "") |
1396 | 1448 | details-width |
1397 | nil ?\s (make-string 1 magit-ellipsis)) | |
1449 | nil ?\s | |
1450 | (if (char-displayable-p ?…) "…" ">")) | |
1398 | 1451 | 'magit-log-author) |
1399 | 1452 | " ")) |
1400 | 1453 | (magit--propertize-face |
1655 | 1708 | (defun magit-insert-unpulled-from-pushremote () |
1656 | 1709 | "Insert commits that haven't been pulled from the push-remote yet." |
1657 | 1710 | (--when-let (magit-get-push-branch) |
1658 | (unless (and (equal (magit-rev-name it) | |
1659 | (magit-rev-name "@{upstream}")) | |
1660 | (or (memq 'magit-insert-unpulled-from-upstream | |
1661 | magit-status-sections-hook) | |
1662 | (memq 'magit-insert-unpulled-from-upstream-or-recent | |
1663 | magit-status-sections-hook))) | |
1711 | (when (magit--insert-pushremote-log-p) | |
1664 | 1712 | (magit-insert-section (unpulled (concat ".." it) t) |
1665 | 1713 | (magit-insert-heading |
1666 | 1714 | (format (propertize "Unpulled from %s." |
1724 | 1772 | (defun magit-insert-unpushed-to-pushremote () |
1725 | 1773 | "Insert commits that haven't been pushed to the push-remote yet." |
1726 | 1774 | (--when-let (magit-get-push-branch) |
1727 | (unless (and (equal (magit-rev-name it) | |
1728 | (magit-rev-name "@{upstream}")) | |
1729 | (or (memq 'magit-insert-unpushed-to-upstream | |
1730 | magit-status-sections-hook) | |
1731 | (memq 'magit-insert-unpushed-to-upstream-or-recent | |
1732 | magit-status-sections-hook))) | |
1775 | (when (magit--insert-pushremote-log-p) | |
1733 | 1776 | (magit-insert-section (unpushed (concat it "..") t) |
1734 | 1777 | (magit-insert-heading |
1735 | 1778 | (format (propertize "Unpushed to %s." |
1737 | 1780 | (propertize it 'font-lock-face 'magit-branch-remote))) |
1738 | 1781 | (magit-insert-log (concat it "..") magit-buffer-log-args) |
1739 | 1782 | (magit-log-insert-child-count))))) |
1783 | ||
1784 | (defun magit--insert-pushremote-log-p () | |
1785 | (magit--with-refresh-cache 'magit--insert-pushremote-log-p | |
1786 | (not (and (equal (magit-get-push-branch) | |
1787 | (magit-get-upstream-branch)) | |
1788 | (or (memq 'magit-insert-unpulled-from-upstream | |
1789 | magit-status-sections-hook) | |
1790 | (memq 'magit-insert-unpulled-from-upstream-or-recent | |
1791 | magit-status-sections-hook)))))) | |
1740 | 1792 | |
1741 | 1793 | (defun magit-log-insert-child-count () |
1742 | 1794 | (when magit-section-show-child-count |
0 | 0 | ;;; magit-margin.el --- margins in Magit buffers -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
29 | 31 | |
30 | 32 | ;;; Code: |
31 | 33 | |
32 | (require 'dash) | |
33 | ||
34 | (eval-when-compile | |
35 | (require 'subr-x)) | |
36 | ||
37 | 34 | (require 'magit-section) |
38 | 35 | (require 'magit-transient) |
39 | 36 | (require 'magit-mode) |
61 | 58 | |
62 | 59 | ;;; Commands |
63 | 60 | |
64 | (define-transient-command magit-margin-settings () | |
61 | (transient-define-prefix magit-margin-settings () | |
65 | 62 | "Change what information is displayed in the margin." |
66 | 63 | :info-manual "(magit) Log Margin" |
67 | 64 | ["Margin" |
0 | 0 | ;;; magit-merge.el --- merge functionality -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | 30 | |
29 | (eval-when-compile | |
30 | (require 'subr-x)) | |
31 | ||
32 | 31 | (require 'magit) |
33 | 32 | (require 'magit-diff) |
34 | 33 | |
37 | 36 | ;;; Commands |
38 | 37 | |
39 | 38 | ;;;###autoload (autoload 'magit-merge "magit" nil t) |
40 | (define-transient-command magit-merge () | |
39 | (transient-define-prefix magit-merge () | |
41 | 40 | "Merge branches." |
42 | 41 | :man-page "git-merge" |
43 | 42 | :incompatible '(("--ff-only" "--no-ff")) |
46 | 45 | ("-f" "Fast-forward only" "--ff-only") |
47 | 46 | ("-n" "No fast-forward" "--no-ff") |
48 | 47 | (magit-merge:--strategy) |
49 | (5 magit-diff:--diff-algorithm :argument "--Xdiff-algorithm=") | |
48 | (5 magit-merge:--strategy-option) | |
49 | (5 magit-diff:--diff-algorithm :argument "-Xdiff-algorithm=") | |
50 | 50 | (5 magit:--gpg-sign)] |
51 | 51 | ["Actions" |
52 | 52 | :if-not magit-merge-in-progress-p |
66 | 66 | (defun magit-merge-arguments () |
67 | 67 | (transient-args 'magit-merge)) |
68 | 68 | |
69 | (define-infix-argument magit-merge:--strategy () | |
69 | (transient-define-argument magit-merge:--strategy () | |
70 | 70 | :description "Strategy" |
71 | 71 | :class 'transient-option |
72 | 72 | ;; key for merge and rebase: "-s" |
76 | 76 | :key "-s" |
77 | 77 | :argument "--strategy=" |
78 | 78 | :choices '("resolve" "recursive" "octopus" "ours" "subtree")) |
79 | ||
80 | (transient-define-argument magit-merge:--strategy-option () | |
81 | :description "Strategy Option" | |
82 | :class 'transient-option | |
83 | :key "-X" | |
84 | :argument "--strategy-option=" | |
85 | :choices '("ours" "theirs" "patience")) | |
79 | 86 | |
80 | 87 | ;;;###autoload |
81 | 88 | (defun magit-merge-plain (rev &optional args nocommit) |
132 | 139 | branch, then also remove the respective remote branch." |
133 | 140 | (interactive |
134 | 141 | (list (magit-read-other-local-branch |
135 | (format "Merge `%s' into" (magit-get-current-branch)) | |
142 | (format "Merge `%s' into" | |
143 | (or (magit-get-current-branch) | |
144 | (magit-rev-parse "HEAD"))) | |
136 | 145 | nil |
137 | 146 | (when-let ((upstream (magit-get-upstream-branch)) |
138 | 147 | (upstream (cdr (magit-split-branch-name upstream)))) |
139 | 148 | (and (magit-branch-p upstream) upstream))) |
140 | 149 | (magit-merge-arguments))) |
141 | (let ((current (magit-get-current-branch))) | |
150 | (let ((current (magit-get-current-branch)) | |
151 | (head (magit-rev-parse "HEAD"))) | |
142 | 152 | (when (zerop (magit-call-git "checkout" branch)) |
143 | (magit--merge-absorb current args)))) | |
153 | (if current | |
154 | (magit--merge-absorb current args) | |
155 | (magit-run-git-with-editor "merge" args head))))) | |
144 | 156 | |
145 | 157 | ;;;###autoload |
146 | 158 | (defun magit-merge-absorb (branch &optional args) |
157 | 169 | (magit--merge-absorb branch args)) |
158 | 170 | |
159 | 171 | (defun magit--merge-absorb (branch args) |
160 | (when (equal branch "master") | |
172 | (when (equal branch (magit-main-branch)) | |
161 | 173 | (unless (yes-or-no-p |
162 | "Do you really want to merge `master' into another branch? ") | |
174 | (format "Do you really want to merge `%s' into another branch? " | |
175 | branch)) | |
163 | 176 | (user-error "Abort"))) |
164 | 177 | (if-let ((target (magit-get-push-branch branch t))) |
165 | 178 | (progn |
182 | 195 | (format "Merge branch '%s'%s [#%s]" |
183 | 196 | branch |
184 | 197 | (let ((current (magit-get-current-branch))) |
185 | (if (equal current "master") "" (format " into %s" current))) | |
198 | (if (equal current (magit-main-branch)) | |
199 | "" | |
200 | (format " into %s" current))) | |
186 | 201 | pr) |
187 | 202 | branch) |
188 | 203 | (magit-run-git-async "merge" args "--no-edit" branch)) |
0 | 0 | ;;; magit-mode.el --- create and refresh Magit buffers -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
28 | 30 | |
29 | 31 | ;;; Code: |
30 | 32 | |
31 | (require 'cl-lib) | |
32 | (require 'dash) | |
33 | ||
34 | (eval-when-compile | |
35 | (require 'subr-x)) | |
36 | ||
37 | (require 'transient) | |
38 | ||
39 | 33 | (require 'magit-section) |
40 | 34 | (require 'magit-git) |
35 | ||
36 | (require 'format-spec) | |
37 | (require 'help-mode) | |
38 | (require 'transient) | |
41 | 39 | |
42 | 40 | ;; For `magit-display-buffer-fullcolumn-most-v1' from `git-commit' |
43 | 41 | (defvar git-commit-mode) |
56 | 54 | ;; For `magit-mode' from `bookmark' |
57 | 55 | (defvar bookmark-make-record-function) |
58 | 56 | |
59 | (require 'format-spec) | |
60 | (require 'help-mode) | |
61 | ||
62 | 57 | ;;; Options |
63 | 58 | |
64 | 59 | (defcustom magit-mode-hook |
234 | 229 | :package-version '(magit . "3.0.0") |
235 | 230 | :group 'magit-buffers |
236 | 231 | :group 'magit-commands |
232 | :group 'magit-diff | |
233 | :group 'magit-log | |
237 | 234 | :type '(choice |
238 | 235 | (const :tag "always use args from buffer" always) |
239 | 236 | (const :tag "use args from buffer if displayed in frame" selected) |
265 | 262 | :package-version '(magit . "3.0.0") |
266 | 263 | :group 'magit-buffers |
267 | 264 | :group 'magit-commands |
265 | :group 'magit-diff | |
266 | :group 'magit-log | |
268 | 267 | :type '(choice |
269 | 268 | (const :tag "always use args from buffer" always) |
270 | 269 | (const :tag "use args from buffer if displayed in frame" selected) |
346 | 345 | (define-key map (kbd "i") 'magit-gitignore) |
347 | 346 | (define-key map (kbd "I") 'magit-gitignore) |
348 | 347 | (define-key map (kbd "SPC") 'magit-diff-show-or-scroll-up) |
348 | (define-key map (kbd "S-SPC") 'magit-diff-show-or-scroll-down) | |
349 | 349 | (define-key map (kbd "DEL") 'magit-diff-show-or-scroll-down) |
350 | 350 | (define-key map "+" 'magit-diff-more-context) |
351 | 351 | (define-key map "-" 'magit-diff-less-context) |
399 | 399 | (define-key map (kbd "C-c C-e") 'magit-edit-thing) |
400 | 400 | (define-key map (kbd "C-c C-o") 'magit-browse-thing) |
401 | 401 | (define-key map (kbd "C-c C-w") 'magit-browse-thing) |
402 | (define-key map (kbd "C-x a") 'magit-add-change-log-entry) | |
403 | (define-key map (kbd "C-x 4 a") 'magit-add-change-log-entry-other-window) | |
404 | 402 | (define-key map (kbd "C-w") 'magit-copy-section-value) |
405 | 403 | (define-key map (kbd "M-w") 'magit-copy-buffer-revision) |
406 | 404 | (define-key map [remap previous-line] 'magit-previous-line) |
422 | 420 | Where applicable, section-specific keymaps bind another command |
423 | 421 | which visits the thing at point." |
424 | 422 | (interactive) |
425 | (if (eq current-transient-command 'magit-dispatch) | |
423 | (if (eq transient-current-command 'magit-dispatch) | |
426 | 424 | (call-interactively (key-binding (this-command-keys))) |
427 | 425 | (user-error "There is no thing at point that could be visited"))) |
428 | 426 | |
431 | 429 | Where applicable, section-specific keymaps bind another command |
432 | 430 | which lets you edit the thing at point, likely in another buffer." |
433 | 431 | (interactive) |
434 | (if (eq current-transient-command 'magit-dispatch) | |
432 | (if (eq transient-current-command 'magit-dispatch) | |
435 | 433 | (call-interactively (key-binding (this-command-keys))) |
436 | 434 | (user-error "There is no thing at point that could be edited"))) |
437 | 435 | |
471 | 469 | ["Cherry pick" magit-cherry-pick t] |
472 | 470 | ["Revert commit" magit-revert t] |
473 | 471 | "---" |
474 | ["Ignore globally" magit-gitignore-globally t] | |
475 | ["Ignore locally" magit-gitignore-locally t] | |
472 | ["Ignore at toplevel" magit-gitignore-in-topdir t] | |
473 | ["Ignore in subdirectory" magit-gitignore-in-subdir t] | |
476 | 474 | ["Discard" magit-discard t] |
477 | 475 | ["Reset head and index" magit-reset-mixed t] |
478 | 476 | ["Stash" magit-stash-both t] |
512 | 510 | Magit is documented in info node `(magit)'." |
513 | 511 | :group 'magit |
514 | 512 | (hack-dir-local-variables-non-file-buffer) |
513 | (face-remap-add-relative 'header-line 'magit-header-line) | |
515 | 514 | (setq mode-line-process (magit-repository-local-get 'mode-line-process)) |
516 | (setq-local bookmark-make-record-function 'magit--make-bookmark)) | |
517 | ||
518 | ;;; Highlighting | |
515 | (setq-local bookmark-make-record-function 'magit--make-bookmark) | |
516 | (setq-local isearch-filter-predicate 'magit-section--open-temporarily)) | |
519 | 517 | |
520 | 518 | ;;; Local Variables |
521 | 519 | |
693 | 691 | (display-buffer |
694 | 692 | buffer (if (with-current-buffer buffer |
695 | 693 | (derived-mode-p 'magit-diff-mode 'magit-process-mode)) |
696 | nil ; display in another window | |
694 | '(nil (inhibit-same-window . t)) | |
697 | 695 | '(display-buffer-same-window)))) |
698 | 696 | |
699 | 697 | (defun magit--display-buffer-fullframe (buffer alist) |
827 | 825 | \(or nil if there is no such buffer) unless VALUE is non-nil, in |
828 | 826 | which case return the buffer that has been looked to that value. |
829 | 827 | |
830 | If FRAME nil or omitted, then consider all buffers. Otherwise | |
828 | If FRAME is nil or omitted, then consider all buffers. Otherwise | |
831 | 829 | only consider buffers that are displayed in some live window |
832 | 830 | on some frame. |
833 | 831 | If `all', then consider all buffers on all frames. |
848 | 846 | (w (window) |
849 | 847 | (b (window-buffer window))) |
850 | 848 | (f (frame) |
851 | (-some #'w (window-list frame 'no-minibuf)))) | |
849 | (seq-some #'w (window-list frame 'no-minibuf)))) | |
852 | 850 | (pcase-exhaustive frame |
853 | (`nil (-some #'b (buffer-list))) | |
854 | (`all (-some #'f (frame-list))) | |
855 | (`visible (-some #'f (visible-frame-list))) | |
856 | ((or `selected `t) (-some #'w (window-list (selected-frame)))) | |
857 | ((guard (framep frame)) (-some #'w (window-list frame))))) | |
851 | (`nil (seq-some #'b (buffer-list))) | |
852 | (`all (seq-some #'f (frame-list))) | |
853 | (`visible (seq-some #'f (visible-frame-list))) | |
854 | ((or `selected `t) (seq-some #'w (window-list (selected-frame)))) | |
855 | ((guard (framep frame)) (seq-some #'w (window-list frame))))) | |
858 | 856 | (magit--not-inside-repository-error))) |
859 | 857 | |
860 | 858 | (defun magit-mode-get-buffer (mode &optional create frame value) |
995 | 993 | |
996 | 994 | ;;; Refresh Buffers |
997 | 995 | |
998 | (defvar inhibit-magit-refresh nil) | |
996 | (defvar magit-inhibit-refresh nil) | |
999 | 997 | |
1000 | 998 | (defun magit-refresh () |
1001 | 999 | "Refresh some buffers belonging to the current repository. |
1005 | 1003 | |
1006 | 1004 | Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." |
1007 | 1005 | (interactive) |
1008 | (unless inhibit-magit-refresh | |
1006 | (unless magit-inhibit-refresh | |
1009 | 1007 | (unwind-protect |
1010 | 1008 | (let ((start (current-time)) |
1011 | 1009 | (magit--refresh-cache (or magit--refresh-cache |
1033 | 1031 | (magit-run-hook-with-benchmark 'magit-post-unstage-hook))) |
1034 | 1032 | (magit-run-hook-with-benchmark 'magit-post-refresh-hook) |
1035 | 1033 | (when magit-refresh-verbose |
1036 | (message "Refreshing magit...done (%.3fs, cached %s/%s)" | |
1037 | (float-time (time-subtract (current-time) start)) | |
1038 | (caar magit--refresh-cache) | |
1039 | (+ (caar magit--refresh-cache) | |
1040 | (cdar magit--refresh-cache))))) | |
1034 | (let* ((c (caar magit--refresh-cache)) | |
1035 | (a (+ c (cdar magit--refresh-cache)))) | |
1036 | (message "Refreshing magit...done (%.3fs, cached %s/%s (%.0f%%))" | |
1037 | (float-time (time-subtract (current-time) start)) | |
1038 | c a (* (/ c (* a 1.0)) 100))))) | |
1041 | 1039 | (run-hooks 'magit-unwind-refresh-hook)))) |
1042 | 1040 | |
1043 | 1041 | (defun magit-refresh-all () |
1067 | 1065 | (when magit-refresh-verbose |
1068 | 1066 | (message "Refreshing buffer `%s'..." (buffer-name))) |
1069 | 1067 | (let* ((buffer (current-buffer)) |
1070 | (windows | |
1071 | (--mapcat (with-selected-window it | |
1072 | (with-current-buffer buffer | |
1073 | (when-let ((section (magit-current-section))) | |
1074 | (list | |
1075 | (nconc (list it section) | |
1076 | (magit-refresh-get-relative-position)))))) | |
1077 | (or (get-buffer-window-list buffer nil t) | |
1078 | (list (selected-window)))))) | |
1068 | (windows (cl-mapcan | |
1069 | (lambda (window) | |
1070 | (with-selected-window window | |
1071 | (with-current-buffer buffer | |
1072 | (when-let ((section (magit-current-section))) | |
1073 | `(( ,window | |
1074 | ,section | |
1075 | ,@(magit-refresh-get-relative-position))))))) | |
1076 | ;; If it qualifies, then the selected window | |
1077 | ;; comes first, but we want to handle it last | |
1078 | ;; so that its `magit-section-movement-hook' | |
1079 | ;; run can override the effects of other runs. | |
1080 | (or (nreverse (get-buffer-window-list buffer nil t)) | |
1081 | (list (selected-window)))))) | |
1079 | 1082 | (deactivate-mark) |
1080 | 1083 | (setq magit-section-highlight-overlays nil) |
1081 | 1084 | (setq magit-section-highlighted-section nil) |
1087 | 1090 | (save-excursion |
1088 | 1091 | (apply refresh (with-no-warnings magit-refresh-args)))) |
1089 | 1092 | (pcase-dolist (`(,window . ,args) windows) |
1090 | (with-selected-window window | |
1093 | (if (eq buffer (window-buffer window)) | |
1094 | (with-selected-window window | |
1095 | (apply #'magit-section-goto-successor args)) | |
1091 | 1096 | (with-current-buffer buffer |
1092 | (apply #'magit-section-goto-successor args)))) | |
1097 | (let ((magit-section-movement-hook nil)) | |
1098 | (apply #'magit-section-goto-successor args))))) | |
1093 | 1099 | (run-hooks 'magit-refresh-buffer-hook) |
1094 | 1100 | (magit-section-update-highlight) |
1095 | 1101 | (set-buffer-modified-p nil)) |
1261 | 1267 | |
1262 | 1268 | (defun magit-insert-xref-buttons () |
1263 | 1269 | "Insert xref buttons." |
1264 | (when (or help-xref-stack help-xref-forward-stack) | |
1270 | (when (and (not magit-buffer-locked-p) | |
1271 | (or help-xref-stack help-xref-forward-stack)) | |
1265 | 1272 | (when help-xref-stack |
1266 | 1273 | (magit-xref-insert-button help-back-label 'magit-xref-backward)) |
1267 | 1274 | (when help-xref-forward-stack |
1343 | 1350 | (defun magit-repository-local-exists-p (key &optional repository) |
1344 | 1351 | "Non-nil when a repository-local value exists for KEY. |
1345 | 1352 | |
1346 | Returns a (KEY . value) cons cell. | |
1353 | Return a (KEY . VALUE) cons cell. | |
1347 | 1354 | |
1348 | 1355 | The KEY is matched using `equal'. |
1349 | 1356 | |
1350 | 1357 | Unless specified, REPOSITORY is the current buffer's repository." |
1351 | (let* ((repokey (or repository (magit-repository-local-repository))) | |
1352 | (cache (assoc repokey magit-repository-local-cache))) | |
1353 | (and cache | |
1354 | (assoc key (cdr cache))))) | |
1358 | (when-let ((cache (assoc (or repository | |
1359 | (magit-repository-local-repository)) | |
1360 | magit-repository-local-cache))) | |
1361 | (assoc key (cdr cache)))) | |
1355 | 1362 | |
1356 | 1363 | (defun magit-repository-local-get (key &optional default repository) |
1357 | 1364 | "Return the repository-local value for KEY. |
1361 | 1368 | The KEY is matched using `equal'. |
1362 | 1369 | |
1363 | 1370 | Unless specified, REPOSITORY is the current buffer's repository." |
1364 | (let ((keyvalue (magit-repository-local-exists-p key repository))) | |
1365 | (if keyvalue | |
1366 | (cdr keyvalue) | |
1367 | default))) | |
1371 | (if-let ((keyvalue (magit-repository-local-exists-p key repository))) | |
1372 | (cdr keyvalue) | |
1373 | default)) | |
1368 | 1374 | |
1369 | 1375 | (defun magit-repository-local-delete (key &optional repository) |
1370 | 1376 | "Delete the repository-local value for KEY. |
1371 | 1377 | |
1372 | 1378 | Unless specified, REPOSITORY is the current buffer's repository." |
1373 | (let* ((repokey (or repository (magit-repository-local-repository))) | |
1374 | (cache (assoc repokey magit-repository-local-cache))) | |
1375 | (when cache | |
1376 | ;; There is no `assoc-delete-all'. | |
1377 | (setf (cdr cache) | |
1378 | (cl-delete key (cdr cache) :key #'car :test #'equal))))) | |
1379 | (when-let ((cache (assoc (or repository | |
1380 | (magit-repository-local-repository)) | |
1381 | magit-repository-local-cache))) | |
1382 | ;; There is no `assoc-delete-all'. | |
1383 | (setf (cdr cache) | |
1384 | (cl-delete key (cdr cache) :key #'car :test #'equal)))) | |
1385 | ||
1386 | (defmacro magit--with-repository-local-cache (key &rest body) | |
1387 | (declare (indent 1) (debug (form body))) | |
1388 | (let ((k (cl-gensym))) | |
1389 | `(let ((,k ,key)) | |
1390 | (if-let ((kv (magit-repository-local-exists-p ,k))) | |
1391 | (cdr kv) | |
1392 | (let ((v ,(macroexp-progn body))) | |
1393 | (magit-repository-local-set ,k v) | |
1394 | v))))) | |
1379 | 1395 | |
1380 | 1396 | (defun magit-preserve-section-visibility-cache () |
1381 | 1397 | (when (derived-mode-p 'magit-status-mode 'magit-refs-mode) |
0 | 0 | ;;; magit-notes.el --- notes support -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
31 | 33 | ;;; Commands |
32 | 34 | |
33 | 35 | ;;;###autoload (autoload 'magit-notes "magit" nil t) |
34 | (define-transient-command magit-notes () | |
36 | (transient-define-prefix magit-notes () | |
35 | 37 | "Edit notes attached to commits." |
36 | 38 | :man-page "git-notes" |
37 | 39 | ["Configure local settings" |
65 | 67 | (and (file-directory-p dir) |
66 | 68 | (directory-files dir nil "^[^.]")))) |
67 | 69 | |
68 | (define-infix-command magit-core.notesRef () | |
70 | (transient-define-infix magit-core.notesRef () | |
69 | 71 | :class 'magit--git-variable |
70 | 72 | :variable "core.notesRef" |
71 | 73 | :reader 'magit-notes-read-ref |
72 | 74 | :prompt "Set local core.notesRef") |
73 | 75 | |
74 | (define-infix-command magit-notes.displayRef () | |
76 | (transient-define-infix magit-notes.displayRef () | |
75 | 77 | :class 'magit--git-variable |
76 | 78 | :variable "notes.displayRef" |
77 | 79 | :multi-value t |
78 | 80 | :reader 'magit-notes-read-refs |
79 | 81 | :prompt "Set local notes.displayRef") |
80 | 82 | |
81 | (define-infix-command magit-global-core.notesRef () | |
83 | (transient-define-infix magit-global-core.notesRef () | |
82 | 84 | :class 'magit--git-variable |
83 | 85 | :variable "core.notesRef" |
84 | 86 | :reader 'magit-notes-read-ref |
85 | 87 | :prompt "Set global core.notesRef") |
86 | 88 | |
87 | (define-infix-command magit-global-notes.displayRef () | |
89 | (transient-define-infix magit-global-notes.displayRef () | |
88 | 90 | :class 'magit--git-variable |
89 | 91 | :variable "notes.displayRef" |
90 | 92 | :multi-value t |
91 | 93 | :reader 'magit-notes-read-refs |
92 | 94 | :prompt "Set global notes.displayRef") |
93 | 95 | |
94 | (define-infix-argument magit-notes:--ref () | |
95 | :description "Merge strategy" | |
96 | (transient-define-argument magit-notes:--ref () | |
97 | :description "Manipulate ref" | |
96 | 98 | :class 'transient-option |
97 | 99 | :key "-r" |
98 | 100 | :argument "--ref=" |
99 | 101 | :reader 'magit-notes-read-ref) |
100 | 102 | |
101 | (define-infix-argument magit-notes:--strategy () | |
103 | (transient-define-argument magit-notes:--strategy () | |
102 | 104 | :description "Merge strategy" |
103 | 105 | :class 'transient-option |
104 | 106 | :shortarg "-s" |
0 | 0 | ;;; magit-obsolete.el --- obsolete definitions -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
50 | 52 | |
51 | 53 | (define-obsolete-variable-alias 'magit-disable-line-numbers |
52 | 54 | 'magit-section-disable-line-numbers "Magit 3.0.0") |
55 | ||
56 | (define-obsolete-variable-alias 'inhibit-magit-refresh | |
57 | 'magit-inhibit-refresh "Magit 3.0.0") | |
53 | 58 | |
54 | 59 | (defun magit--magit-popup-warning () |
55 | 60 | (display-warning 'magit "\ |
0 | 0 | ;;; magit-patch.el --- creating and applying patches -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
25 | 27 | ;; This library implements patch commands. |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | ||
29 | (eval-when-compile | |
30 | (require 'subr-x)) | |
31 | 30 | |
32 | 31 | (require 'magit) |
33 | 32 | |
54 | 53 | ;;; Commands |
55 | 54 | |
56 | 55 | ;;;###autoload (autoload 'magit-patch "magit-patch" nil t) |
57 | (define-transient-command magit-patch () | |
56 | (transient-define-prefix magit-patch () | |
58 | 57 | "Create or apply patches." |
59 | 58 | ["Actions" |
60 | 59 | ("c" "Create patches" magit-patch-create) |
63 | 62 | ("r" "Request pull" magit-request-pull)]) |
64 | 63 | |
65 | 64 | ;;;###autoload (autoload 'magit-patch-create "magit-patch" nil t) |
66 | (define-transient-command magit-patch-create (range args files) | |
65 | (transient-define-prefix magit-patch-create (range args files) | |
67 | 66 | "Create patches for the commits in RANGE. |
68 | 67 | When a single commit is given for RANGE, create a patch for the |
69 | 68 | changes introduced by that commit (unlike 'git format-patch' |
99 | 98 | ["Actions" |
100 | 99 | ("c" "Create patches" magit-patch-create)] |
101 | 100 | (interactive |
102 | (if (not (eq current-transient-command 'magit-patch-create)) | |
101 | (if (not (eq transient-current-command 'magit-patch-create)) | |
103 | 102 | (list nil nil nil) |
104 | 103 | (cons (if-let ((revs (magit-region-values 'commit t))) |
105 | 104 | (concat (car (last revs)) "^.." (car revs)) |
118 | 117 | (save-match-data |
119 | 118 | (find-file |
120 | 119 | (expand-file-name |
121 | (concat (--some (and (string-match "\\`--reroll-count=\\(.+\\)" it) | |
122 | (format "v%s-" (match-string 1 it))) | |
123 | args) | |
120 | (concat (when-let ((v (transient-arg-value "--reroll-count=" args))) | |
121 | (format "v%s-" v)) | |
124 | 122 | "0000-cover-letter.patch") |
125 | 123 | (let ((topdir (magit-toplevel))) |
126 | (or (--some (and (string-match "\\`--output-directory=\\(.+\\)" it) | |
127 | (expand-file-name (match-string 1 it) topdir)) | |
128 | args) | |
129 | topdir)))))))) | |
130 | ||
131 | (define-infix-argument magit-format-patch:--in-reply-to () | |
124 | (if-let ((dir (transient-arg-value "--output-directory=" args))) | |
125 | (expand-file-name dir topdir) | |
126 | topdir)))))))) | |
127 | ||
128 | (transient-define-argument magit-format-patch:--in-reply-to () | |
132 | 129 | :description "In reply to" |
133 | 130 | :class 'transient-option |
134 | 131 | :key "C-m C-r" |
135 | 132 | :argument "--in-reply-to=") |
136 | 133 | |
137 | (define-infix-argument magit-format-patch:--thread () | |
134 | (transient-define-argument magit-format-patch:--thread () | |
138 | 135 | :description "Thread style" |
139 | 136 | :class 'transient-option |
140 | 137 | :key "C-m s " |
146 | 143 | (?d "[d]eep" "deep") |
147 | 144 | (?s "[s]hallow" "shallow"))) |
148 | 145 | |
149 | (define-infix-argument magit-format-patch:--base () | |
146 | (transient-define-argument magit-format-patch:--base () | |
150 | 147 | :description "Insert base commit" |
151 | 148 | :class 'transient-option |
152 | 149 | :key "C-m b " |
158 | 155 | nil nil initial-input history "auto") |
159 | 156 | (user-error "Nothing selected"))) |
160 | 157 | |
161 | (define-infix-argument magit-format-patch:--reroll-count () | |
158 | (transient-define-argument magit-format-patch:--reroll-count () | |
162 | 159 | :description "Reroll count" |
163 | 160 | :class 'transient-option |
164 | 161 | :key "C-m v " |
166 | 163 | :argument "--reroll-count=" |
167 | 164 | :reader 'transient-read-number-N+) |
168 | 165 | |
169 | (define-infix-argument magit-format-patch:--interdiff () | |
166 | (transient-define-argument magit-format-patch:--interdiff () | |
170 | 167 | :description "Insert interdiff" |
171 | 168 | :class 'transient-option |
172 | 169 | :key "C-m d i" |
173 | 170 | :argument "--interdiff=" |
174 | 171 | :reader #'magit-transient-read-revision) |
175 | 172 | |
176 | (define-infix-argument magit-format-patch:--range-diff () | |
173 | (transient-define-argument magit-format-patch:--range-diff () | |
177 | 174 | :description "Insert range-diff" |
178 | 175 | :class 'transient-option |
179 | 176 | :key "C-m d r" |
183 | 180 | (defun magit-format-patch-select-range-diff (prompt _initial-input _history) |
184 | 181 | (magit-read-range-or-commit prompt)) |
185 | 182 | |
186 | (define-infix-argument magit-format-patch:--subject-prefix () | |
183 | (transient-define-argument magit-format-patch:--subject-prefix () | |
187 | 184 | :description "Subject Prefix" |
188 | 185 | :class 'transient-option |
189 | 186 | :key "C-m p " |
190 | 187 | :argument "--subject-prefix=") |
191 | 188 | |
192 | (define-infix-argument magit-format-patch:--cover-from-description () | |
189 | (transient-define-argument magit-format-patch:--cover-from-description () | |
193 | 190 | :description "Use branch description" |
194 | 191 | :class 'transient-option |
195 | 192 | :key "C-m D " |
203 | 200 | (?a "[a]uto" "auto") |
204 | 201 | (?n "[n]othing" "none"))) |
205 | 202 | |
206 | (define-infix-argument magit-format-patch:--notes () | |
203 | (transient-define-argument magit-format-patch:--notes () | |
207 | 204 | :description "Insert commentary from notes" |
208 | 205 | :class 'transient-option |
209 | 206 | :key "C-m n " |
210 | 207 | :argument "--notes=" |
211 | 208 | :reader #'magit-notes-read-ref) |
212 | 209 | |
213 | (define-infix-argument magit-format-patch:--from () | |
210 | (transient-define-argument magit-format-patch:--from () | |
214 | 211 | :description "From" |
215 | 212 | :class 'transient-option |
216 | 213 | :key "C-m C-f" |
217 | 214 | :argument "--from=" |
218 | 215 | :reader 'magit-transient-read-person) |
219 | 216 | |
220 | (define-infix-argument magit-format-patch:--to () | |
217 | (transient-define-argument magit-format-patch:--to () | |
221 | 218 | :description "To" |
222 | 219 | :class 'transient-option |
223 | 220 | :key "C-m C-t" |
224 | 221 | :argument "--to=" |
225 | 222 | :reader 'magit-transient-read-person) |
226 | 223 | |
227 | (define-infix-argument magit-format-patch:--cc () | |
224 | (transient-define-argument magit-format-patch:--cc () | |
228 | 225 | :description "CC" |
229 | 226 | :class 'transient-option |
230 | 227 | :key "C-m C-c" |
231 | 228 | :argument "--cc=" |
232 | 229 | :reader 'magit-transient-read-person) |
233 | 230 | |
234 | (define-infix-argument magit-format-patch:--output-directory () | |
231 | (transient-define-argument magit-format-patch:--output-directory () | |
235 | 232 | :description "Output directory" |
236 | 233 | :class 'transient-option |
237 | 234 | :key "C-m o " |
240 | 237 | :reader 'transient-read-existing-directory) |
241 | 238 | |
242 | 239 | ;;;###autoload (autoload 'magit-patch-apply "magit-patch" nil t) |
243 | (define-transient-command magit-patch-apply (file &rest args) | |
240 | (transient-define-prefix magit-patch-apply (file &rest args) | |
244 | 241 | "Apply the patch file FILE." |
245 | 242 | :man-page "git-apply" |
246 | 243 | ["Arguments" |
250 | 247 | ["Actions" |
251 | 248 | ("a" "Apply patch" magit-patch-apply)] |
252 | 249 | (interactive |
253 | (if (not (eq current-transient-command 'magit-patch-apply)) | |
250 | (if (not (eq transient-current-command 'magit-patch-apply)) | |
254 | 251 | (list nil) |
255 | 252 | (list (expand-file-name |
256 | 253 | (read-file-name "Apply patch: " |
307 | 304 | |
308 | 305 | ;;;###autoload |
309 | 306 | (defun magit-request-pull (url start end) |
310 | "Request upstream to pull from you public repository. | |
307 | "Request upstream to pull from your public repository. | |
311 | 308 | |
312 | 309 | URL is the url of your publicly accessible repository. |
313 | 310 | START is a commit that already is in the upstream repository. |
0 | (define-package "magit" "2.90.1" | |
0 | (define-package "magit" "3.0.0" | |
1 | 1 | "A Git porcelain inside Emacs." |
2 | 2 | '((emacs "25.1") |
3 | (async "20180527") | |
4 | (dash "20180910") | |
5 | (git-commit "20181104") | |
6 | (transient "20190812") | |
7 | (with-editor "20181103")) | |
8 | :keywords | |
9 | '("git" "tools" "vc")) | |
3 | (dash "2.18.1") | |
4 | (git-commit "3.0.0") | |
5 | (magit-section "3.0.0") | |
6 | (transient "0.3.3") | |
7 | (with-editor "3.0.4")) | |
8 | :homepage "https://magit.vc" | |
9 | :keywords '("git" "tools" "vc")) |
0 | 0 | ;;; magit-process.el --- process functionality -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
30 | 32 | |
31 | 33 | ;;; Code: |
32 | 34 | |
33 | (require 'ansi-color) | |
34 | (require 'cl-lib) | |
35 | (require 'dash) | |
36 | ||
37 | (eval-when-compile | |
38 | (require 'subr-x)) | |
39 | ||
40 | (require 'with-editor) | |
41 | 35 | (require 'magit-utils) |
42 | 36 | (require 'magit-section) |
43 | 37 | (require 'magit-git) |
44 | 38 | (require 'magit-mode) |
39 | ||
40 | (require 'ansi-color) | |
41 | (require 'with-editor) | |
45 | 42 | |
46 | 43 | (declare-function auth-source-search "auth-source" |
47 | 44 | (&rest spec &key max require create delete &allow-other-keys)) |
97 | 94 | :package-version '(magit . "2.1.0") |
98 | 95 | :group 'magit-process |
99 | 96 | :type '(choice (const :tag "Never remove old sections" nil) integer)) |
97 | ||
98 | (defvar magit-process-extreme-logging nil | |
99 | "Whether `magit-process-file' logs to *Messages* buffer. | |
100 | Only intended for temporary use when you try to figure out how | |
101 | Magit uses Git behind the scene. Output that normally goes to | |
102 | the magit-process buffer continues to go there. Not all output | |
103 | goes to either of these two buffers.") | |
100 | 104 | |
101 | 105 | (defcustom magit-process-error-tooltip-max-lines 20 |
102 | 106 | "The number of lines for `magit-process-error-lines' to return. |
155 | 159 | "\\([Nn]o?\\)" |
156 | 160 | ;; OpenSSH v8 prints this. See #3969. |
157 | 161 | "\\(?:/\\[fingerprint\\]\\)?" |
158 | "[\])] ?[?:] ?$") | |
162 | "[\])] ?[?:]? ?$") | |
159 | 163 | "Regexp matching Yes-or-No prompts of Git and its subprocesses." |
160 | 164 | :package-version '(magit . "2.1.0") |
161 | 165 | :group 'magit-process |
168 | 172 | "Please enter the passphrase for the ssh key" |
169 | 173 | "Please enter the passphrase to unlock the OpenPGP secret key" |
170 | 174 | "^.*'s password: ?$" |
175 | "^Token: $" ; For git-credential-manager-core (#4318). | |
171 | 176 | "^Yubikey for .*: ?$" |
172 | 177 | "^Enter PIN for .*: ?$") |
173 | 178 | "List of regexps matching password prompts of Git and its subprocesses. |
174 | 179 | Also see `magit-process-find-password-functions'." |
175 | :package-version '(magit . "2.8.0") | |
180 | :package-version '(magit . "3.0.0") | |
176 | 181 | :group 'magit-process |
177 | 182 | :type '(repeat (regexp))) |
178 | 183 | |
397 | 402 | Identical to `process-file' but temporarily enable Cygwin's |
398 | 403 | \"noglob\" option during the call and ensure unix eol |
399 | 404 | conversion." |
405 | (when magit-process-extreme-logging | |
406 | (let ((inhibit-message t)) | |
407 | (message "$ %s" (magit-process--format-arguments process args)))) | |
400 | 408 | (let ((process-environment (magit-process-environment)) |
401 | 409 | (default-process-coding-system (magit--process-coding-system))) |
402 | 410 | (apply #'process-file process infile buffer display args))) |
425 | 433 | "Call Git in a separate process. |
426 | 434 | ARGS is flattened and then used as arguments to Git. |
427 | 435 | |
428 | The current buffer's content is used as the process' standard | |
429 | input. | |
436 | The current buffer's content is used as the process's standard | |
437 | input. The buffer is assumed to be temporary and thus OK to | |
438 | modify. | |
430 | 439 | |
431 | 440 | Option `magit-git-executable' specifies the Git executable and |
432 | 441 | option `magit-git-global-arguments' specifies constant arguments. |
569 | 578 | (when (eq system-type 'windows-nt) |
570 | 579 | ;; On w32, git expects UTF-8 encoded input, ignore any user |
571 | 580 | ;; configuration telling us otherwise. |
572 | (set-process-coding-system process 'utf-8-unix)) | |
581 | (set-process-coding-system process nil 'utf-8-unix)) | |
573 | 582 | (process-put process 'section section) |
574 | 583 | (process-put process 'command-buf (current-buffer)) |
575 | 584 | (process-put process 'default-dir default-directory) |
576 | (when inhibit-magit-refresh | |
585 | (when magit-inhibit-refresh | |
577 | 586 | (process-put process 'inhibit-refresh t)) |
578 | 587 | (oset section process process) |
579 | 588 | (with-current-buffer process-buf |
629 | 638 | (unless (equal (expand-file-name pwd) |
630 | 639 | (expand-file-name default-directory)) |
631 | 640 | (insert (file-relative-name pwd default-directory) ?\s)) |
632 | (cond | |
633 | ((and args (equal program magit-git-executable)) | |
634 | (setq args (-split-at (length magit-git-global-arguments) args)) | |
635 | (insert (propertize (file-name-nondirectory program) | |
636 | 'font-lock-face 'magit-section-heading) " ") | |
637 | (insert (propertize (char-to-string magit-ellipsis) | |
638 | 'font-lock-face 'magit-section-heading | |
639 | 'help-echo (mapconcat #'identity (car args) " "))) | |
640 | (insert " ") | |
641 | (insert (propertize (mapconcat #'shell-quote-argument (cadr args) " ") | |
642 | 'font-lock-face 'magit-section-heading))) | |
643 | ((and args (equal program shell-file-name)) | |
644 | (insert (propertize (cadr args) | |
645 | 'font-lock-face 'magit-section-heading))) | |
646 | (t | |
647 | (insert (propertize (file-name-nondirectory program) | |
648 | 'font-lock-face 'magit-section-heading) " ") | |
649 | (insert (propertize (mapconcat #'shell-quote-argument args " ") | |
650 | 'font-lock-face 'magit-section-heading)))) | |
641 | (insert (magit-process--format-arguments program args)) | |
651 | 642 | (magit-insert-heading) |
652 | 643 | (when errlog |
653 | 644 | (if (bufferp errlog) |
656 | 647 | (insert-file-contents errlog) |
657 | 648 | (goto-char (1- (point-max))))) |
658 | 649 | (insert "\n")))) |
650 | ||
651 | (defun magit-process--format-arguments (program args) | |
652 | (cond | |
653 | ((and args (equal program magit-git-executable)) | |
654 | (setq args (-split-at (length magit-git-global-arguments) args)) | |
655 | (concat (propertize (file-name-nondirectory program) | |
656 | 'font-lock-face 'magit-section-heading) | |
657 | " " | |
658 | (propertize (if (stringp magit-ellipsis) | |
659 | magit-ellipsis | |
660 | ;; For backward compatibility. | |
661 | (char-to-string magit-ellipsis)) | |
662 | 'font-lock-face 'magit-section-heading | |
663 | 'help-echo (mapconcat #'identity (car args) " ")) | |
664 | " " | |
665 | (propertize (mapconcat #'shell-quote-argument (cadr args) " ") | |
666 | 'font-lock-face 'magit-section-heading))) | |
667 | ((and args (equal program shell-file-name)) | |
668 | (propertize (cadr args) | |
669 | 'font-lock-face 'magit-section-heading)) | |
670 | (t | |
671 | (concat (propertize (file-name-nondirectory program) | |
672 | 'font-lock-face 'magit-section-heading) | |
673 | " " | |
674 | (propertize (mapconcat #'shell-quote-argument args " ") | |
675 | 'font-lock-face 'magit-section-heading))))) | |
659 | 676 | |
660 | 677 | (defun magit-process-truncate-log () |
661 | 678 | (let* ((head nil) |
774 | 791 | 'magit-process-password-auth-source) |
775 | 792 | |
776 | 793 | KEY typically derives from a prompt such as: |
777 | Password for 'https://tarsius@bitbucket.org' | |
794 | Password for 'https://yourname@github.com' | |
778 | 795 | in which case it would be the string |
779 | tarsius@bitbucket.org | |
796 | yourname@github.com | |
780 | 797 | which matches the ~/.authinfo.gpg entry |
781 | machine bitbucket.org login tarsius password 12345 | |
798 | machine github.com login yourname password 12345 | |
782 | 799 | or iff that is undefined, for backward compatibility |
783 | machine tarsius@bitbucket.org password 12345" | |
800 | machine yourname@github.com password 12345 | |
801 | ||
802 | On github.com you should not use your password but a | |
803 | personal access token, see [1]. For information about | |
804 | the peculiarities of other forges, please consult the | |
805 | respective documentation. | |
806 | ||
807 | After manually editing ~/.authinfo.gpg you must reset | |
808 | the cache using | |
809 | M-x auth-source-forget-all-cached RET | |
810 | ||
811 | The above will save you from having to repeatedly type | |
812 | your token or password, but you might still repeatedly | |
813 | be asked for your username. To prevent that, change an | |
814 | URL like | |
815 | https://github.com/foo/bar.git | |
816 | to | |
817 | https://yourname@github.com/foo/bar.git | |
818 | ||
819 | Instead of changing all such URLs manually, they can | |
820 | be translated on the fly by doing this once | |
821 | git config --global \ | |
822 | url.https://yourname@github.com.insteadOf \ | |
823 | https://github.com | |
824 | ||
825 | [1]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token." | |
784 | 826 | (require 'auth-source) |
785 | 827 | (and (string-match "\\`\\(.+\\)@\\([^@]+\\)\\'" key) |
786 | 828 | (let* ((user (match-string 1 key)) |
793 | 835 | (if (functionp secret) |
794 | 836 | (funcall secret) |
795 | 837 | secret)))) |
838 | ||
839 | (defun magit-process-git-credential-manager-core (process string) | |
840 | "Authenticate using `git-credential-manager-core'. | |
841 | ||
842 | To use this function add it to the appropriate hook | |
843 | (add-hook 'magit-process-prompt-functions | |
844 | 'magit-process-git-credential-manager-core)" | |
845 | (and (string-match "^option (enter for default): $" string) | |
846 | (progn | |
847 | (magit-process-buffer) | |
848 | (process-send-string | |
849 | process | |
850 | (format "%c" (read-char-choice "Option: " '(?\r ?\j ?1 ?2))))))) | |
796 | 851 | |
797 | 852 | (defun magit-process-password-prompt (process string) |
798 | 853 | "Find a password based on prompt STRING and send it to git. |
955 | 1010 | ;; The following closure captures the repokey value, and is |
956 | 1011 | ;; added to `pre-command-hook'. |
957 | 1012 | (cl-labels ((enable-magit-process-unset-mode-line |
958 | () ;; Remove ourself from the hook variable, so | |
959 | ;; that we only run once. | |
1013 | () ;;; Remove ourself from the hook variable, so | |
1014 | ;;; that we only run once. | |
960 | 1015 | (remove-hook 'pre-command-hook |
961 | 1016 | #'enable-magit-process-unset-mode-line) |
962 | 1017 | ;; Clear the inhibit flag for the repository in |
0 | 0 | ;;; magit-pull.el --- update local objects and refs -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
39 | 41 | ;;; Commands |
40 | 42 | |
41 | 43 | ;;;###autoload (autoload 'magit-pull "magit-pull" nil t) |
42 | (define-transient-command magit-pull () | |
44 | (transient-define-prefix magit-pull () | |
43 | 45 | "Pull from another repository." |
44 | 46 | :man-page "git-pull" |
45 | 47 | [:description |
76 | 78 | (transient-args 'magit-pull)) |
77 | 79 | |
78 | 80 | ;;;###autoload (autoload 'magit-pull-from-pushremote "magit-pull" nil t) |
79 | (define-suffix-command magit-pull-from-pushremote (args) | |
81 | (transient-define-suffix magit-pull-from-pushremote (args) | |
80 | 82 | "Pull from the push-remote of the current branch. |
81 | 83 | |
82 | 84 | With a prefix argument or when the push-remote is either not |
106 | 108 | (format "%s, setting that" v))))) |
107 | 109 | |
108 | 110 | ;;;###autoload (autoload 'magit-pull-from-upstream "magit-pull" nil t) |
109 | (define-suffix-command magit-pull-from-upstream (args) | |
111 | (transient-define-suffix magit-pull-from-upstream (args) | |
110 | 112 | "Pull from the upstream of the current branch. |
111 | 113 | |
112 | 114 | With a prefix argument or when the upstream is either not |
0 | 0 | ;;; magit-push.el --- update remote objects and refs -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | 30 | |
29 | (eval-when-compile | |
30 | (require 'subr-x)) | |
31 | ||
32 | 31 | (require 'magit) |
33 | 32 | |
34 | 33 | ;;; Commands |
35 | 34 | |
36 | 35 | ;;;###autoload (autoload 'magit-push "magit-push" nil t) |
37 | (define-transient-command magit-push () | |
36 | (transient-define-prefix magit-push () | |
38 | 37 | "Push to another repository." |
39 | 38 | :man-page "git-push" |
40 | 39 | ["Arguments" |
77 | 76 | (format "%s:%s%s" branch namespace target)))) |
78 | 77 | |
79 | 78 | ;;;###autoload (autoload 'magit-push-current-to-pushremote "magit-push" nil t) |
80 | (define-suffix-command magit-push-current-to-pushremote (args) | |
79 | (transient-define-suffix magit-push-current-to-pushremote (args) | |
81 | 80 | "Push the current branch to its push-remote. |
82 | 81 | |
83 | 82 | When the push-remote is not configured, then read the push-remote |
86 | 85 | :if 'magit-get-current-branch |
87 | 86 | :description 'magit-push--pushbranch-description |
88 | 87 | (interactive (list (magit-push-arguments))) |
89 | (pcase-let ((`(,branch ,remote) | |
88 | (pcase-let ((`(,branch ,remote ,changed) | |
90 | 89 | (magit--select-push-remote "push there"))) |
90 | (when changed | |
91 | (magit-confirm 'set-and-push | |
92 | (format "Really use \"%s\" as push-remote and push \"%s\" there" | |
93 | remote branch))) | |
91 | 94 | (run-hooks 'magit-credential-hook) |
92 | 95 | (magit-run-git-async "push" "-v" args remote |
93 | 96 | (format "refs/heads/%s:refs/heads/%s" |
110 | 113 | (format "%s, setting that" v))))) |
111 | 114 | |
112 | 115 | ;;;###autoload (autoload 'magit-push-current-to-upstream "magit-push" nil t) |
113 | (define-suffix-command magit-push-current-to-upstream (args) | |
116 | (transient-define-suffix magit-push-current-to-upstream (args) | |
114 | 117 | "Push the current branch to its upstream branch. |
115 | 118 | |
116 | 119 | With a prefix argument or when the upstream is either not |
135 | 138 | branches nil nil nil 'magit-revision-history |
136 | 139 | (or (car (member (magit-remote-branch-at-point) branches)) |
137 | 140 | (car (member "origin/master" branches))))) |
138 | (upstream (or (magit-get-tracked upstream) | |
139 | (magit-split-branch-name upstream)))) | |
140 | (setq remote (car upstream)) | |
141 | (setq merge (cdr upstream)) | |
141 | (upstream* (or (magit-get-tracked upstream) | |
142 | (magit-split-branch-name upstream)))) | |
143 | (setq remote (car upstream*)) | |
144 | (setq merge (cdr upstream*)) | |
142 | 145 | (unless (string-prefix-p "refs/" merge) |
143 | 146 | ;; User selected a non-existent remote-tracking branch. |
144 | 147 | ;; It is very likely, but not certain, that this is the |
145 | 148 | ;; correct thing to do. It is even more likely that it |
146 | 149 | ;; is what the user wants to happen. |
147 | (setq merge (concat "refs/heads/" merge)))) | |
150 | (setq merge (concat "refs/heads/" merge))) | |
151 | (magit-confirm 'set-and-push | |
152 | (format "Really use \"%s\" as upstream and push \"%s\" there" | |
153 | upstream branch))) | |
148 | 154 | (cl-pushnew "--set-upstream" args :test #'equal)) |
149 | 155 | (run-hooks 'magit-credential-hook) |
150 | 156 | (magit-run-git-async "push" "-v" args remote (concat branch ":" merge)))) |
261 | 267 | (run-hooks 'magit-credential-hook) |
262 | 268 | (magit-run-git-async "push" remote ref args)) |
263 | 269 | |
264 | ;;;###autoload | |
265 | (defun magit-push-implicitly (args) | |
270 | ;;;###autoload (autoload 'magit-push-implicitly "magit-push" nil t) | |
271 | (transient-define-suffix magit-push-implicitly (args) | |
266 | 272 | "Push somewhere without using an explicit refspec. |
267 | 273 | |
268 | 274 | This command simply runs \"git push -v [ARGS]\". ARGS are the |
272 | 278 | `branch.<branch>.pushRemote', `branch.<branch>.remote', |
273 | 279 | `branch.<branch>.merge', and `remote.<remote>.push'. |
274 | 280 | |
275 | The function `magit-push-implicitly--desc' attempts to predict | |
276 | what this command will do. The value it returns is displayed in | |
277 | the popup buffer." | |
281 | If you add this suffix to a transient prefix without explicitly | |
282 | specifying the description, then an attempt is made to predict | |
283 | what this command will do. For example: | |
284 | ||
285 | (transient-insert-suffix 'magit-push \"p\" | |
286 | '(\"i\" magit-push-implicitly))" | |
287 | :description 'magit-push-implicitly--desc | |
278 | 288 | (interactive (list (magit-push-arguments))) |
279 | 289 | (run-hooks 'magit-credential-hook) |
280 | 290 | (magit-run-git-async "push" "-v" args)) |
283 | 293 | (let ((default (magit-get "push.default"))) |
284 | 294 | (unless (equal default "nothing") |
285 | 295 | (or (when-let ((remote (or (magit-get-remote) |
286 | (magit-remote-p "origin"))) | |
296 | (magit-primary-remote))) | |
287 | 297 | (refspec (magit-get "remote" remote "push"))) |
288 | 298 | (format "%s using %s" |
289 | 299 | (magit--propertize-face remote 'magit-branch-remote) |
0 | 0 | ;;; magit-reflog.el --- inspect ref history -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
28 | 30 | |
29 | 31 | (require 'magit-core) |
30 | 32 | (require 'magit-log) |
31 | ||
32 | (eval-when-compile | |
33 | (require 'subr-x)) | |
34 | 33 | |
35 | 34 | ;;; Options |
36 | 35 |
0 | 0 | ;;; magit-refs.el --- listing references -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
25 | 27 | ;; This library implements support for listing references in a buffer. |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | ||
29 | (eval-when-compile | |
30 | (require 'subr-x)) | |
31 | 30 | |
32 | 31 | (require 'magit) |
33 | 32 | |
326 | 325 | ;;; Commands |
327 | 326 | |
328 | 327 | ;;;###autoload (autoload 'magit-show-refs "magit-refs" nil t) |
329 | (define-transient-command magit-show-refs (&optional transient) | |
328 | (transient-define-prefix magit-show-refs (&optional transient) | |
330 | 329 | "List and compare references in a dedicated buffer." |
331 | 330 | :man-page "git-branch" |
332 | 331 | :value (lambda () |
333 | 332 | (magit-show-refs-arguments magit-prefix-use-buffer-arguments)) |
334 | 333 | ["Arguments" |
335 | 334 | (magit-for-each-ref:--contains) |
336 | ("=m" "Merged" "--merged=" magit-transient-read-revision) | |
335 | ("-M" "Merged" "--merged=" magit-transient-read-revision) | |
337 | 336 | ("-m" "Merged to HEAD" "--merged") |
338 | ("-M" "Merged to master" "--merged=master") | |
339 | ("=n" "Not merged" "--no-merged=" magit-transient-read-revision) | |
337 | ("-N" "Not merged" "--no-merged=" magit-transient-read-revision) | |
340 | 338 | ("-n" "Not merged to HEAD" "--no-merged") |
341 | ("-N" "Not merged to master" "--no-merged=master") | |
342 | 339 | (magit-for-each-ref:--sort)] |
343 | 340 | ["Actions" |
344 | 341 | ("y" "Show refs, comparing them with HEAD" magit-show-refs-head) |
355 | 352 | (setq use-buffer-args magit-direct-use-buffer-arguments)) |
356 | 353 | (let (args) |
357 | 354 | (cond |
358 | ((eq current-transient-command 'magit-show-refs) | |
355 | ((eq transient-current-command 'magit-show-refs) | |
359 | 356 | (setq args (transient-args 'magit-show-refs))) |
360 | 357 | ((eq major-mode 'magit-refs-mode) |
361 | 358 | (setq args magit-buffer-arguments)) |
369 | 366 | (setq args (alist-get 'magit-show-refs transient-values)))) |
370 | 367 | args)) |
371 | 368 | |
372 | (define-infix-argument magit-for-each-ref:--contains () | |
369 | (transient-define-argument magit-for-each-ref:--contains () | |
373 | 370 | :description "Contains" |
374 | 371 | :class 'transient-option |
375 | 372 | :key "-c" |
376 | 373 | :argument "--contains=" |
377 | 374 | :reader 'magit-transient-read-revision) |
378 | 375 | |
379 | (define-infix-argument magit-for-each-ref:--sort () | |
376 | (transient-define-argument magit-for-each-ref:--sort () | |
380 | 377 | :description "Sort" |
381 | 378 | :class 'transient-option |
382 | 379 | :key "-s" |
0 | 0 | ;;; magit-remote.el --- transfer Git commits -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
63 | 65 | ;;; Commands |
64 | 66 | |
65 | 67 | ;;;###autoload (autoload 'magit-remote "magit-remote" nil t) |
66 | (define-transient-command magit-remote (remote) | |
68 | (transient-define-prefix magit-remote (remote) | |
67 | 69 | "Add, configure or remove a remote." |
68 | 70 | :man-page "git-remote" |
69 | 71 | :value '("-f") |
97 | 99 | ;;;###autoload |
98 | 100 | (defun magit-remote-add (remote url &optional args) |
99 | 101 | "Add a remote named REMOTE and fetch it." |
100 | (interactive (list (magit-read-string-ns "Remote name") | |
101 | (magit-read-url "Remote url") | |
102 | (transient-args 'magit-remote))) | |
102 | (interactive | |
103 | (let ((origin (magit-get "remote.origin.url")) | |
104 | (remote (magit-read-string-ns "Remote name"))) | |
105 | (list remote | |
106 | (magit-read-url | |
107 | "Remote url" | |
108 | (and origin | |
109 | (string-match "\\([^:/]+\\)/[^/]+\\(\\.git\\)?\\'" origin) | |
110 | (replace-match remote t t origin 1))) | |
111 | (transient-args 'magit-remote)))) | |
103 | 112 | (if (pcase (list magit-remote-add-set-remote.pushDefault |
104 | 113 | (magit-get "remote.pushDefault")) |
105 | 114 | (`(,(pred stringp) ,_) t) |
250 | 259 | ;;; Configure |
251 | 260 | |
252 | 261 | ;;;###autoload (autoload 'magit-remote-configure "magit-remote" nil t) |
253 | (define-transient-command magit-remote-configure (remote) | |
262 | (transient-define-prefix magit-remote-configure (remote) | |
254 | 263 | "Configure a remote." |
255 | 264 | :man-page "git-remote" |
256 | 265 | [:description |
266 | 275 | (interactive |
267 | 276 | (list (or (and (not current-prefix-arg) |
268 | 277 | (not (and magit-remote-direct-configure |
269 | (eq current-transient-command 'magit-remote))) | |
278 | (eq transient-current-command 'magit-remote))) | |
270 | 279 | (magit-get-current-remote)) |
271 | 280 | (magit--read-remote-scope)))) |
272 | 281 | (transient-setup 'magit-remote-configure nil nil :scope remote)) |
278 | 287 | (format (oref obj variable) "<name>")) |
279 | 288 | "Configure remote"))) |
280 | 289 | |
281 | (define-infix-command magit-remote.<remote>.url () | |
290 | (transient-define-infix magit-remote.<remote>.url () | |
282 | 291 | :class 'magit--git-variable:urls |
283 | 292 | :scope 'magit--read-remote-scope |
284 | 293 | :variable "remote.%s.url" |
285 | 294 | :multi-value t |
286 | 295 | :history-key 'magit-remote.<remote>.*url) |
287 | 296 | |
288 | (define-infix-command magit-remote.<remote>.fetch () | |
297 | (transient-define-infix magit-remote.<remote>.fetch () | |
289 | 298 | :class 'magit--git-variable |
290 | 299 | :scope 'magit--read-remote-scope |
291 | 300 | :variable "remote.%s.fetch" |
292 | 301 | :multi-value t) |
293 | 302 | |
294 | (define-infix-command magit-remote.<remote>.pushurl () | |
303 | (transient-define-infix magit-remote.<remote>.pushurl () | |
295 | 304 | :class 'magit--git-variable:urls |
296 | 305 | :scope 'magit--read-remote-scope |
297 | 306 | :variable "remote.%s.pushurl" |
299 | 308 | :history-key 'magit-remote.<remote>.*url |
300 | 309 | :seturl-arg "--push") |
301 | 310 | |
302 | (define-infix-command magit-remote.<remote>.push () | |
311 | (transient-define-infix magit-remote.<remote>.push () | |
303 | 312 | :class 'magit--git-variable |
304 | 313 | :scope 'magit--read-remote-scope |
305 | 314 | :variable "remote.%s.push") |
306 | 315 | |
307 | (define-infix-command magit-remote.<remote>.tagopt () | |
316 | (transient-define-infix magit-remote.<remote>.tagopt () | |
308 | 317 | :class 'magit--git-variable:choices |
309 | 318 | :scope 'magit--read-remote-scope |
310 | 319 | :variable "remote.%s.tagOpt" |
324 | 333 | (defun magit--select-push-remote (prompt-suffix) |
325 | 334 | (let* ((branch (or (magit-get-current-branch) |
326 | 335 | (user-error "No branch is checked out"))) |
327 | (remote (magit-get-push-remote branch))) | |
336 | (remote (magit-get-push-remote branch)) | |
337 | (changed nil)) | |
328 | 338 | (when (or current-prefix-arg |
329 | 339 | (not remote) |
330 | 340 | (not (member remote (magit-list-remotes)))) |
341 | (setq changed t) | |
331 | 342 | (setq remote |
332 | 343 | (magit-read-remote (format "Set %s and %s" |
333 | 344 | (magit--push-remote-variable) |
334 | 345 | prompt-suffix))) |
335 | 346 | (setf (magit-get (magit--push-remote-variable branch)) remote)) |
336 | (list branch remote))) | |
347 | (list branch remote changed))) | |
337 | 348 | |
338 | 349 | ;;; _ |
339 | 350 | (provide 'magit-remote) |
0 | 0 | ;;; magit-repos.el --- listing repositories -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
27 | 29 | ;; mode for listing repositories in a buffer. |
28 | 30 | |
29 | 31 | ;;; Code: |
30 | ||
31 | (eval-when-compile | |
32 | (require 'subr-x)) | |
33 | 32 | |
34 | 33 | (require 'magit-core) |
35 | 34 | |
156 | 155 | "Major mode for browsing a list of Git repositories." |
157 | 156 | (setq-local x-stretch-cursor nil) |
158 | 157 | (setq tabulated-list-padding 0) |
159 | (setq tabulated-list-sort-key (cons "Path" nil)) | |
158 | (setq tabulated-list-sort-key | |
159 | (cons (or (car (assoc "Path" magit-repolist-columns)) | |
160 | (caar magit-repolist-columns)) | |
161 | nil)) | |
160 | 162 | (setq tabulated-list-format |
161 | 163 | (vconcat (mapcar (pcase-lambda (`(,title ,width ,_fn ,props)) |
162 | 164 | (nconc (list title width t) |
221 | 223 | - U if there is at least one unstaged file. |
222 | 224 | - S if there is at least one staged file. |
223 | 225 | Only one letter is shown, the first that applies." |
224 | (-some (pcase-lambda (`(,fun . ,flag)) | |
225 | (and (funcall fun) flag)) | |
226 | magit-repolist-column-flag-alist)) | |
226 | (seq-some (pcase-lambda (`(,fun . ,flag)) | |
227 | (and (funcall fun) flag)) | |
228 | magit-repolist-column-flag-alist)) | |
227 | 229 | |
228 | 230 | (defun magit-repolist-column-unpulled-from-upstream (_id) |
229 | 231 | "Insert number of upstream commits not in the current branch." |
0 | 0 | ;;; magit-reset.el --- reset fuctionality -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
29 | 31 | (require 'magit) |
30 | 32 | |
31 | 33 | ;;;###autoload (autoload 'magit-reset "magit" nil t) |
32 | (define-transient-command magit-reset () | |
34 | (transient-define-prefix magit-reset () | |
33 | 35 | "Reset the `HEAD', index and/or worktree to a previous state." |
34 | 36 | :man-page "git-reset" |
35 | 37 | ["Reset" |
36 | 38 | ("m" "mixed (HEAD and index)" magit-reset-mixed) |
37 | 39 | ("s" "soft (HEAD only)" magit-reset-soft) |
38 | 40 | ("h" "hard (HEAD, index and files)" magit-reset-hard) |
41 | ("k" "keep (HEAD and index, keeping uncommitted)" magit-reset-keep) | |
39 | 42 | ("i" "index (only)" magit-reset-index) |
40 | 43 | ("w" "worktree (only)" magit-reset-worktree) |
41 | 44 | "" |
63 | 66 | (concat (magit--propertize-face "Hard" 'bold) |
64 | 67 | " reset %s to")))) |
65 | 68 | (magit-reset-internal "--hard" commit)) |
69 | ||
70 | ;;;###autoload | |
71 | (defun magit-reset-keep (commit) | |
72 | "Reset the `HEAD' and index to COMMIT, while keeping uncommitted changes. | |
73 | \n(git reset --keep REVISION)" | |
74 | (interactive (list (magit-reset-read-branch-or-commit "Reset %s to"))) | |
75 | (magit-reset-internal "--keep" commit)) | |
66 | 76 | |
67 | 77 | ;;;###autoload |
68 | 78 | (defun magit-reset-index (commit) |
0 | 0 | ;;; magit-section.el --- Sections for read-only buffers -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | 9 | |
10 | ;; Package-Requires: ((emacs "25.1") (dash "20180910")) | |
11 | 10 | ;; Keywords: tools |
12 | 11 | ;; Homepage: https://github.com/magit/magit |
12 | ;; Package-Requires: ((emacs "25.1") (dash "2.18.1")) | |
13 | ;; Package-Version: 3.0.0 | |
14 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
13 | 15 | |
14 | 16 | ;; Magit-Section is free software; you can redistribute it and/or modify |
15 | 17 | ;; it under the terms of the GNU General Public License as published by |
36 | 38 | (require 'cl-lib) |
37 | 39 | (require 'dash) |
38 | 40 | (require 'eieio) |
39 | ||
40 | (eval-when-compile | |
41 | (require 'benchmark) | |
42 | (require 'subr-x)) | |
41 | (require 'seq) | |
42 | (require 'subr-x) | |
43 | ||
44 | (eval-when-compile (require 'benchmark)) | |
43 | 45 | |
44 | 46 | ;;; Hooks |
45 | 47 | |
136 | 138 | (defcustom magit-section-visibility-indicator |
137 | 139 | (if (window-system) |
138 | 140 | '(magit-fringe-bitmap> . magit-fringe-bitmapv) |
139 | '("…" . t)) | |
141 | (cons (if (char-displayable-p ?…) "…" "...") | |
142 | t)) | |
140 | 143 | "Whether and how to indicate that a section can be expanded/collapsed. |
141 | 144 | |
142 | 145 | If nil, then don't show any indicators. |
683 | 686 | (cond ((and (--any-p (oref it hidden) children) |
684 | 687 | (--any-p (oref it children) children)) |
685 | 688 | (magit-section-show-headings section)) |
686 | ((-any-p 'magit-section-hidden-body children) | |
689 | ((seq-some 'magit-section-hidden-body children) | |
687 | 690 | (magit-section-show-children section)) |
688 | 691 | (t |
689 | 692 | (magit-section-hide section)))))) |
695 | 698 | (cond ((and (--any-p (oref it hidden) children) |
696 | 699 | (--any-p (oref it children) children)) |
697 | 700 | (magit-section-show-headings magit-root-section)) |
698 | ((-any-p 'magit-section-hidden-body children) | |
701 | ((seq-some 'magit-section-hidden-body children) | |
699 | 702 | (magit-section-show-children magit-root-section)) |
700 | 703 | (t |
701 | 704 | (mapc 'magit-section-hide children))))) |
780 | 783 | With a prefix argument show the section identity instead of the |
781 | 784 | section lineage. This command is intended for debugging purposes." |
782 | 785 | (interactive (list (magit-current-section) current-prefix-arg)) |
783 | (let ((str (format "#<%s %S %S %s-%s>" | |
786 | (let ((str (format "#<%s %S %S %s-%s%s>" | |
784 | 787 | (eieio-object-class section) |
785 | 788 | (let ((val (oref section value))) |
786 | 789 | (cond ((stringp val) |
795 | 798 | (apply #'vector (magit-section-lineage section))) |
796 | 799 | (when-let ((m (oref section start))) |
797 | 800 | (marker-position m)) |
801 | (if-let ((m (oref section content))) | |
802 | (format "[%s-]" (marker-position m)) | |
803 | "") | |
798 | 804 | (when-let ((m (oref section end))) |
799 | 805 | (marker-position m))))) |
800 | 806 | (if (called-interactively-p 'any) |
970 | 976 | |
971 | 977 | (defun magit-section-match-assoc (section alist) |
972 | 978 | "Return the value associated with SECTION's type or lineage in ALIST." |
973 | (-some (pcase-lambda (`(,key . ,val)) | |
974 | (and (magit-section-match-1 key section) val)) | |
975 | alist)) | |
979 | (seq-some (pcase-lambda (`(,key . ,val)) | |
980 | (and (magit-section-match-1 key section) val)) | |
981 | alist)) | |
976 | 982 | |
977 | 983 | ;;; Create |
978 | 984 | |
984 | 990 | (defmacro magit-insert-section (&rest args) |
985 | 991 | "Insert a section at point. |
986 | 992 | |
987 | TYPE is the section type, a symbol which is prefixed with the | |
988 | name of the package. (For historic reasons the types used by | |
989 | Magit and Forge do not use a package prefix.) Many commands | |
990 | that act on the current section behave differently depending | |
991 | on its type. | |
992 | ||
993 | Optional VALUE is the value of the section, usually a string | |
994 | that is required when acting on the section. | |
993 | Create a section object of type CLASS, storing VALUE in its | |
994 | `value' slot, and insert the section at point. CLASS is a | |
995 | subclass of `magit-section' or has the form `(eval FORM)', in | |
996 | which case FORM is evaluated at runtime and should return a | |
997 | subclass. In other places a sections class is oftern referred | |
998 | to as its \"type\". | |
999 | ||
1000 | Many commands behave differently depending on the class of the | |
1001 | current section and sections of a certain class can have their | |
1002 | own keymap, which is specified using the `keymap' class slot. | |
1003 | The value of that slot should be a variable whose value is a | |
1004 | keymap. | |
1005 | ||
1006 | For historic reasons Magit and Forge in most cases use symbols | |
1007 | as CLASS that don't actually identify a class and that lack the | |
1008 | appropriate package prefix. This works due to some undocumented | |
1009 | kludges, which are not available to other packages. | |
995 | 1010 | |
996 | 1011 | When optional HIDE is non-nil collapse the section body by |
997 | 1012 | default, i.e. when first creating the section, but not when |
1017 | 1032 | a section by washing Git's output and Git didn't actually output |
1018 | 1033 | anything this time around. |
1019 | 1034 | |
1020 | For historic reasons, if a variable `magit-TYPE-section-map' | |
1021 | or `forge-TYPE-section-map' exists, then use that as the | |
1022 | text-property `keymap' of all text belonging to the section (but | |
1023 | this may be overwritten in subsections). TYPE can also have the | |
1024 | form `(eval FORM)' in which case FORM is evaluated at runtime. | |
1025 | ||
1026 | \(fn [NAME] (TYPE &optional VALUE HIDE) &rest BODY)" | |
1035 | \(fn [NAME] (CLASS &optional VALUE HIDE) &rest BODY)" | |
1027 | 1036 | (declare (indent defun) |
1028 | 1037 | (debug ([&optional symbolp] |
1029 | (&or [("eval" symbolp) &optional form form] | |
1038 | (&or [("eval" form) &optional form form] | |
1030 | 1039 | [symbolp &optional form form]) |
1031 | 1040 | body))) |
1032 | 1041 | (let ((tp (cl-gensym "type")) |
1042 | 1051 | (or (cdr (assq ,tp magit--section-type-alist)) |
1043 | 1052 | 'magit-section)) |
1044 | 1053 | :type |
1045 | (if (class-p ,tp) | |
1046 | (or (car (rassq ,tp magit--section-type-alist)) | |
1047 | (error "BUG: No entry for %s in %s" ,tp | |
1048 | 'magit--section-type-alist)) | |
1049 | ,tp) | |
1054 | (or (and (class-p ,tp) | |
1055 | (car (rassq ,tp magit--section-type-alist))) | |
1056 | ,tp) | |
1050 | 1057 | :value ,(nth 1 (car args)) |
1051 | 1058 | :start (point-marker) |
1052 | 1059 | :parent magit-insert-section--parent))) |
1053 | 1060 | (oset ,s hidden |
1054 | (if-let ((value (run-hook-with-args-until-success | |
1055 | 'magit-section-set-visibility-hook ,s))) | |
1056 | (eq value 'hide) | |
1057 | (if-let ((incarnation (and magit-insert-section--oldroot | |
1058 | (magit-get-section | |
1059 | (magit-section-ident ,s) | |
1060 | magit-insert-section--oldroot)))) | |
1061 | (oref incarnation hidden) | |
1062 | (if-let ((value (magit-section-match-assoc | |
1063 | ,s magit-section-initial-visibility-alist))) | |
1064 | (progn | |
1065 | (when (functionp value) | |
1066 | (setq value (funcall value ,s))) | |
1067 | (eq value 'hide)) | |
1068 | ,(nth 2 (car args)))))) | |
1061 | (let ((value (run-hook-with-args-until-success | |
1062 | 'magit-section-set-visibility-hook ,s))) | |
1063 | (if value | |
1064 | (eq value 'hide) | |
1065 | (let ((incarnation (and magit-insert-section--oldroot | |
1066 | (magit-get-section | |
1067 | (magit-section-ident ,s) | |
1068 | magit-insert-section--oldroot)))) | |
1069 | (if incarnation | |
1070 | (oref incarnation hidden) | |
1071 | (let ((value (magit-section-match-assoc | |
1072 | ,s magit-section-initial-visibility-alist))) | |
1073 | (if value | |
1074 | (progn | |
1075 | (when (functionp value) | |
1076 | (setq value (funcall value ,s))) | |
1077 | (eq value 'hide)) | |
1078 | ,(nth 2 (car args))))))))) | |
1069 | 1079 | (let ((magit-insert-section--current ,s) |
1070 | 1080 | (magit-insert-section--parent ,s) |
1071 | 1081 | (magit-insert-section--oldroot |
1432 | 1442 | (if (oref section hidden) |
1433 | 1443 | (car magit-section-visibility-indicator) |
1434 | 1444 | (cdr magit-section-visibility-indicator)) |
1435 | (face-foreground 'fringe)))))) | |
1445 | 'fringe))))) | |
1436 | 1446 | ((stringp (car-safe magit-section-visibility-indicator)) |
1437 | 1447 | (let ((ov (magit--overlay-at (1- eoh) 'magit-vis-indicator 'eoh))) |
1438 | 1448 | (cond ((oref section hidden) |
1490 | 1500 | (1+ (line-end-position))))) |
1491 | 1501 | (when (overlay-get o 'magit-vis-indicator) |
1492 | 1502 | (delete-overlay o))))) |
1503 | ||
1504 | (defvar-local magit-section--opened-sections nil) | |
1505 | ||
1506 | (defun magit-section--open-temporarily (beg end) | |
1507 | (save-excursion | |
1508 | (goto-char beg) | |
1509 | (let ((section (magit-current-section))) | |
1510 | (while section | |
1511 | (let ((content (oref section content))) | |
1512 | (if (and (magit-section-invisible-p section) | |
1513 | (<= (or content (oref section start)) | |
1514 | beg | |
1515 | (oref section end))) | |
1516 | (progn | |
1517 | (when content | |
1518 | (magit-section-show section) | |
1519 | (push section magit-section--opened-sections)) | |
1520 | (setq section (oref section parent))) | |
1521 | (setq section nil)))))) | |
1522 | (or (eq search-invisible t) | |
1523 | (not (isearch-range-invisible beg end)))) | |
1524 | ||
1525 | (defun isearch-clean-overlays@magit-mode (fn) | |
1526 | (if (derived-mode-p 'magit-mode) | |
1527 | (let ((pos (point))) | |
1528 | (dolist (section magit-section--opened-sections) | |
1529 | (unless (<= (oref section content) pos (oref section end)) | |
1530 | (magit-section-hide section))) | |
1531 | (setq magit-section--opened-sections nil)) | |
1532 | (funcall fn))) | |
1533 | ||
1534 | (advice-add 'isearch-clean-overlays :around | |
1535 | 'isearch-clean-overlays@magit-mode) | |
1493 | 1536 | |
1494 | 1537 | ;;; Utilities |
1495 | 1538 | |
1713 | 1756 | magit--current-section-hook))) |
1714 | 1757 | (unless (memq entry magit-disabled-section-inserters) |
1715 | 1758 | (if (bound-and-true-p magit-refresh-verbose) |
1716 | (message " %-50s %s" entry | |
1717 | (benchmark-elapse (apply entry args))) | |
1759 | (let ((time (benchmark-elapse (apply entry args)))) | |
1760 | (message " %-50s %s %s" entry time | |
1761 | (cond ((> time 0.03) "!!") | |
1762 | ((> time 0.01) "!") | |
1763 | (t "")))) | |
1718 | 1764 | (apply entry args))))))) |
1719 | 1765 | |
1720 | 1766 | (cl-defun magit--overlay-at (pos prop &optional (val nil sval) testfn) |
1727 | 1773 | val))))) |
1728 | 1774 | (overlays-at pos t))) |
1729 | 1775 | |
1776 | ;;; Bitmaps | |
1777 | ||
1778 | (when (fboundp 'define-fringe-bitmap) | |
1779 | (define-fringe-bitmap 'magit-fringe-bitmap+ | |
1780 | [#b00000000 | |
1781 | #b00011000 | |
1782 | #b00011000 | |
1783 | #b01111110 | |
1784 | #b01111110 | |
1785 | #b00011000 | |
1786 | #b00011000 | |
1787 | #b00000000]) | |
1788 | (define-fringe-bitmap 'magit-fringe-bitmap- | |
1789 | [#b00000000 | |
1790 | #b00000000 | |
1791 | #b00000000 | |
1792 | #b01111110 | |
1793 | #b01111110 | |
1794 | #b00000000 | |
1795 | #b00000000 | |
1796 | #b00000000]) | |
1797 | ||
1798 | (define-fringe-bitmap 'magit-fringe-bitmap> | |
1799 | [#b01100000 | |
1800 | #b00110000 | |
1801 | #b00011000 | |
1802 | #b00001100 | |
1803 | #b00011000 | |
1804 | #b00110000 | |
1805 | #b01100000 | |
1806 | #b00000000]) | |
1807 | (define-fringe-bitmap 'magit-fringe-bitmapv | |
1808 | [#b00000000 | |
1809 | #b10000010 | |
1810 | #b11000110 | |
1811 | #b01101100 | |
1812 | #b00111000 | |
1813 | #b00010000 | |
1814 | #b00000000 | |
1815 | #b00000000]) | |
1816 | ||
1817 | (define-fringe-bitmap 'magit-fringe-bitmap-bold> | |
1818 | [#b11100000 | |
1819 | #b01110000 | |
1820 | #b00111000 | |
1821 | #b00011100 | |
1822 | #b00011100 | |
1823 | #b00111000 | |
1824 | #b01110000 | |
1825 | #b11100000]) | |
1826 | (define-fringe-bitmap 'magit-fringe-bitmap-boldv | |
1827 | [#b10000001 | |
1828 | #b11000011 | |
1829 | #b11100111 | |
1830 | #b01111110 | |
1831 | #b00111100 | |
1832 | #b00011000 | |
1833 | #b00000000 | |
1834 | #b00000000]) | |
1835 | ) | |
1836 | ||
1730 | 1837 | ;;; _ |
1731 | 1838 | (provide 'magit-section) |
1732 | 1839 | ;;; magit-section.el ends here |
0 | 0 | ;;; magit-sequence.el --- history manipulation in Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2011-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2011-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
28 | 30 | |
29 | 31 | ;;; Code: |
30 | 32 | |
31 | (eval-when-compile | |
32 | (require 'subr-x)) | |
33 | ||
34 | 33 | (require 'magit) |
35 | 34 | |
36 | 35 | ;; For `magit-rebase--todo'. |
130 | 129 | "The Perl executable.") |
131 | 130 | |
132 | 131 | ;;;###autoload (autoload 'magit-cherry-pick "magit-sequence" nil t) |
133 | (define-transient-command magit-cherry-pick () | |
132 | (transient-define-prefix magit-cherry-pick () | |
134 | 133 | "Apply or transplant commits." |
135 | 134 | :man-page "git-cherry-pick" |
136 | 135 | :value '("--ff") |
159 | 158 | ("s" "Skip" magit-sequencer-skip) |
160 | 159 | ("a" "Abort" magit-sequencer-abort)]) |
161 | 160 | |
162 | (define-infix-argument magit-cherry-pick:--mainline () | |
161 | (transient-define-argument magit-cherry-pick:--mainline () | |
163 | 162 | :description "Replay merge relative to parent" |
164 | 163 | :class 'transient-option |
165 | 164 | :shortarg "-m" |
350 | 349 | ;;; Revert |
351 | 350 | |
352 | 351 | ;;;###autoload (autoload 'magit-revert "magit-sequence" nil t) |
353 | (define-transient-command magit-revert () | |
352 | (transient-define-prefix magit-revert () | |
354 | 353 | "Revert existing commits, with or without creating new commits." |
355 | 354 | :man-page "git-revert" |
356 | 355 | :value '("--edit") |
402 | 401 | ;;; Patch |
403 | 402 | |
404 | 403 | ;;;###autoload (autoload 'magit-am "magit-sequence" nil t) |
405 | (define-transient-command magit-am () | |
404 | (transient-define-prefix magit-am () | |
406 | 405 | "Apply patches received by email." |
407 | 406 | :man-page "git-am" |
408 | 407 | :value '("--3way") |
431 | 430 | (defun magit-am-arguments () |
432 | 431 | (transient-args 'magit-am)) |
433 | 432 | |
434 | (define-infix-argument magit-apply:-p () | |
433 | (transient-define-argument magit-apply:-p () | |
435 | 434 | :description "Remove leading slashes from paths" |
436 | 435 | :class 'transient-option |
437 | 436 | :argument "-p" |
494 | 493 | ;;; Rebase |
495 | 494 | |
496 | 495 | ;;;###autoload (autoload 'magit-rebase "magit-sequence" nil t) |
497 | (define-transient-command magit-rebase () | |
496 | (transient-define-prefix magit-rebase () | |
498 | 497 | "Transplant commits and/or modify existing commits." |
499 | 498 | :man-page "git-rebase" |
500 | 499 | ["Arguments" |
502 | 501 | ("-k" "Keep empty commits" "--keep-empty") |
503 | 502 | ("-p" "Preserve merges" ("-p" "--preserve-merges")) |
504 | 503 | (7 magit-merge:--strategy) |
504 | (7 magit-merge:--strategy-option) | |
505 | (7 "=X" magit-diff:--diff-algorithm :argument "-Xdiff-algorithm=") | |
505 | 506 | ("-d" "Lie about committer date" "--committer-date-is-author-date") |
506 | 507 | ("-a" "Autosquash" "--autosquash") |
507 | 508 | ("-A" "Autostash" "--autostash") |
534 | 535 | ("e" "Edit" magit-rebase-edit) |
535 | 536 | ("a" "Abort" magit-rebase-abort)]) |
536 | 537 | |
537 | (define-infix-argument magit-rebase:--exec () | |
538 | (transient-define-argument magit-rebase:--exec () | |
538 | 539 | :description "Run command after commits" |
539 | 540 | :class 'transient-option |
540 | 541 | :shortarg "-x" |
553 | 554 | (magit-run-git-sequencer "rebase" args target)) |
554 | 555 | |
555 | 556 | ;;;###autoload (autoload 'magit-rebase-onto-pushremote "magit-sequence" nil t) |
556 | (define-suffix-command magit-rebase-onto-pushremote (args) | |
557 | (transient-define-suffix magit-rebase-onto-pushremote (args) | |
557 | 558 | "Rebase the current branch onto its push-remote branch. |
558 | 559 | |
559 | 560 | With a prefix argument or when the push-remote is either not |
567 | 568 | (magit-git-rebase (concat remote "/" branch) args))) |
568 | 569 | |
569 | 570 | ;;;###autoload (autoload 'magit-rebase-onto-upstream "magit-sequence" nil t) |
570 | (define-suffix-command magit-rebase-onto-upstream (args) | |
571 | (transient-define-suffix magit-rebase-onto-upstream (args) | |
571 | 572 | "Rebase the current branch onto its upstream branch. |
572 | 573 | |
573 | 574 | With a prefix argument or when the upstream is either not |
666 | 667 | (unless (member "--root" args) commit))) |
667 | 668 | (magit-log-select |
668 | 669 | `(lambda (commit) |
669 | (magit-rebase-interactive-1 commit (list ,@args) | |
670 | ,message ,editor ,delay-edit-confirm ,noassert)) | |
670 | ;; In some cases (currently just magit-rebase-remove-commit), "-c | |
671 | ;; commentChar=#" is added to the global arguments for git. Ensure | |
672 | ;; that the same happens when we chose the commit via | |
673 | ;; magit-log-select, below. | |
674 | (let ((magit-git-global-arguments (list ,@magit-git-global-arguments))) | |
675 | (magit-rebase-interactive-1 commit (list ,@args) | |
676 | ,message ,editor ,delay-edit-confirm ,noassert))) | |
671 | 677 | message))) |
672 | 678 | |
673 | 679 | (defvar magit--rebase-published-symbol nil) |
752 | 758 | "Remove a single older commit using rebase." |
753 | 759 | (interactive (list (magit-commit-at-point) |
754 | 760 | (magit-rebase-arguments))) |
755 | (magit-rebase-interactive-1 commit args | |
756 | "Type %p on a commit to remove it," | |
757 | (apply-partially #'magit-rebase--perl-editor 'remove) | |
758 | nil nil t)) | |
761 | ;; magit-rebase--perl-editor assumes that the comment character is "#". | |
762 | (let ((magit-git-global-arguments | |
763 | (nconc (list "-c" "core.commentChar=#") | |
764 | magit-git-global-arguments))) | |
765 | (magit-rebase-interactive-1 commit args | |
766 | "Type %p on a commit to remove it," | |
767 | (apply-partially #'magit-rebase--perl-editor 'remove) | |
768 | nil nil t))) | |
759 | 769 | |
760 | 770 | (defun magit-rebase--perl-editor (action since) |
761 | 771 | (let ((commit (magit-rev-abbrev (magit-rebase--target-commit since)))) |
764 | 774 | commit |
765 | 775 | (cl-case action |
766 | 776 | (edit "edit") |
767 | (remove "# pick") | |
777 | (remove "noop\n# pick") | |
768 | 778 | (reword "reword") |
769 | 779 | (t (error "unknown action: %s" action))) |
770 | 780 | commit))) |
782 | 792 | (file-exists-p (magit-git-dir "rebase-merge")) |
783 | 793 | (not (member (magit-toplevel) |
784 | 794 | magit--rebase-public-edit-confirmed))) |
785 | (magit-commit-amend-assert)) | |
795 | (magit-commit-amend-assert | |
796 | (magit-file-line (magit-git-dir "rebase-merge/orig-head")))) | |
786 | 797 | (if noedit |
787 | 798 | (let ((process-environment process-environment)) |
788 | 799 | (push "GIT_EDITOR=true" process-environment) |
898 | 909 | (when (magit-rebase-in-progress-p) |
899 | 910 | (let* ((interactive (file-directory-p (magit-git-dir "rebase-merge"))) |
900 | 911 | (dir (if interactive "rebase-merge/" "rebase-apply/")) |
901 | (name (-> (concat dir "head-name") magit-git-dir magit-file-line)) | |
902 | (onto (-> (concat dir "onto") magit-git-dir magit-file-line)) | |
912 | (name (thread-first (concat dir "head-name") | |
913 | magit-git-dir | |
914 | magit-file-line)) | |
915 | (onto (thread-first (concat dir "onto") | |
916 | magit-git-dir | |
917 | magit-file-line)) | |
903 | 918 | (onto (or (magit-rev-name onto name) |
904 | 919 | (magit-rev-name onto "refs/heads/*") onto)) |
905 | 920 | (name (or (magit-rev-name name "refs/heads/*") name))) |
0 | 0 | ;;; magit-stash.el --- stash support for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | 30 | |
29 | (eval-when-compile | |
30 | (require 'subr-x)) | |
31 | ||
32 | 31 | (require 'magit) |
33 | 32 | (require 'magit-reflog) |
33 | ||
34 | ;; For `magit-stash-drop'. | |
35 | (defvar helm-comp-read-use-marked) | |
34 | 36 | |
35 | 37 | ;;; Options |
36 | 38 | |
85 | 87 | ;;; Commands |
86 | 88 | |
87 | 89 | ;;;###autoload (autoload 'magit-stash "magit-stash" nil t) |
88 | (define-transient-command magit-stash () | |
90 | (transient-define-prefix magit-stash () | |
89 | 91 | "Stash uncommitted changes." |
90 | 92 | :man-page "git-stash" |
91 | 93 | ["Arguments" |
230 | 232 | (interactive |
231 | 233 | (list (--if-let (magit-region-values 'stash) |
232 | 234 | (magit-confirm 'drop-stashes nil "Drop %i stashes" nil it) |
233 | (magit-read-stash "Drop stash")))) | |
235 | (let ((helm-comp-read-use-marked t)) | |
236 | (magit-read-stash "Drop stash"))))) | |
234 | 237 | (dolist (stash (if (listp stash) |
235 | 238 | (nreverse (prog1 stash (setq stash (car stash)))) |
236 | 239 | (list stash))) |
268 | 271 | current branch or `HEAD' as the start-point." |
269 | 272 | (interactive (list (magit-read-stash "Branch stash") |
270 | 273 | (magit-read-string-ns "Branch name"))) |
271 | (let ((inhibit-magit-refresh t)) | |
274 | (let ((magit-inhibit-refresh t)) | |
272 | 275 | (magit-branch-and-checkout branch (or (magit-get-current-branch) "HEAD"))) |
273 | 276 | (magit-stash-apply stash)) |
274 | 277 | |
374 | 377 | (let ((verified (magit-rev-verify ref)) |
375 | 378 | (autostash |
376 | 379 | (and (magit-rebase-in-progress-p) |
377 | (magit-file-line | |
378 | (magit-git-dir | |
379 | (-> (if (file-directory-p (magit-git-dir "rebase-merge")) | |
380 | "rebase-merge/autostash" | |
381 | "rebase-apply/autostash"))))))) | |
380 | (thread-first | |
381 | (if (file-directory-p (magit-git-dir "rebase-merge")) | |
382 | "rebase-merge/autostash" | |
383 | "rebase-apply/autostash") | |
384 | magit-git-dir | |
385 | magit-file-line)))) | |
382 | 386 | (when (or autostash verified) |
383 | 387 | (magit-insert-section (stashes ref) |
384 | 388 | (magit-insert-heading heading) |
0 | 0 | ;;; magit-status.el --- the grand overview -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
25 | 27 | ;; This library implements the status buffer. |
26 | 28 | |
27 | 29 | ;;; Code: |
28 | ||
29 | (eval-when-compile | |
30 | (require 'subr-x)) | |
31 | 30 | |
32 | 31 | (require 'magit) |
33 | 32 | |
175 | 174 | :set-after '(magit-log-margin) |
176 | 175 | :set (apply-partially #'magit-margin-set-variable 'magit-status-mode)) |
177 | 176 | |
177 | (defcustom magit-status-use-buffer-arguments 'selected | |
178 | "Whether `magit-status' reuses arguments when the buffer already exists. | |
179 | ||
180 | This option has no effect when merely refreshing the status | |
181 | buffer using `magit-refresh'. | |
182 | ||
183 | Valid values are: | |
184 | ||
185 | `always': Always use the set of arguments that is currently | |
186 | active in the status buffer, provided that buffer exists | |
187 | of course. | |
188 | `selected': Use the set of arguments from the status | |
189 | buffer, but only if it is displayed in a window of the | |
190 | current frame. This is the default. | |
191 | `current': Use the set of arguments from the status buffer, | |
192 | but only if it is the current buffer. | |
193 | `never': Never use the set of arguments from the status | |
194 | buffer." | |
195 | :package-version '(magit . "3.0.0") | |
196 | :group 'magit-buffers | |
197 | :group 'magit-commands | |
198 | :type '(choice | |
199 | (const :tag "always use args from buffer" always) | |
200 | (const :tag "use args from buffer if displayed in frame" selected) | |
201 | (const :tag "use args from buffer if it is current" current) | |
202 | (const :tag "never use args from buffer" never))) | |
203 | ||
178 | 204 | ;;; Commands |
179 | 205 | |
180 | 206 | ;;;###autoload |
241 | 267 | (interactive |
242 | 268 | (let ((magit--refresh-cache (list (cons 0 0)))) |
243 | 269 | (list (and (or current-prefix-arg (not (magit-toplevel))) |
244 | (magit-read-repository | |
245 | (>= (prefix-numeric-value current-prefix-arg) 16))) | |
270 | (progn (magit--assert-usable-git) | |
271 | (magit-read-repository | |
272 | (>= (prefix-numeric-value current-prefix-arg) 16)))) | |
246 | 273 | magit--refresh-cache))) |
247 | 274 | (let ((magit--refresh-cache (or cache (list (cons 0 0))))) |
248 | 275 | (if directory |
289 | 316 | (if-let ((version (let ((default-directory directory)) |
290 | 317 | (magit-git-version)))) |
291 | 318 | (if (version<= magit--minimal-git version) |
292 | (push version magit--remotes-using-recent-git) | |
319 | (push remote magit--remotes-using-recent-git) | |
293 | 320 | (display-warning 'magit (format "\ |
294 | 321 | Magit requires Git >= %s, but on %s the version is %s. |
295 | 322 | |
326 | 353 | map) |
327 | 354 | "Keymap for `magit-status-mode'.") |
328 | 355 | |
329 | (define-transient-command magit-status-jump () | |
356 | (transient-define-prefix magit-status-jump () | |
330 | 357 | "In a Magit-Status buffer, jump to a section." |
331 | 358 | ["Jump to" |
332 | 359 | [("z " "Stashes" magit-jump-to-stashes |
343 | 370 | :if (lambda () (memq 'magit-insert-unpulled-from-upstream magit-status-sections-hook))) |
344 | 371 | ("fp" "Unpulled from pushremote" magit-jump-to-unpulled-from-pushremote |
345 | 372 | :if (lambda () (memq 'magit-insert-unpulled-from-pushremote magit-status-sections-hook))) |
346 | ("pu" "Unpushed to upstream" magit-jump-to-unpushed-to-upstream | |
373 | ("pu" magit-jump-to-unpushed-to-upstream | |
347 | 374 | :if (lambda () |
348 | 375 | (or (memq 'magit-insert-unpushed-to-upstream-or-recent magit-status-sections-hook) |
349 | (memq 'magit-insert-unpushed-to-upstream magit-status-sections-hook)))) | |
376 | (memq 'magit-insert-unpushed-to-upstream magit-status-sections-hook))) | |
377 | :description (lambda () | |
378 | (let ((upstream (magit-get-upstream-branch))) | |
379 | (if (or (not upstream) | |
380 | (magit-rev-ancestor-p "HEAD" upstream)) | |
381 | "Recent commits" | |
382 | "Unmerged into upstream")))) | |
350 | 383 | ("pp" "Unpushed to pushremote" magit-jump-to-unpushed-to-pushremote |
351 | 384 | :if (lambda () (memq 'magit-insert-unpushed-to-pushremote magit-status-sections-hook))) |
352 | 385 | ("a " "Assumed unstaged" magit-jump-to-assume-unchanged |
396 | 429 | (setq directory default-directory)) |
397 | 430 | (magit--tramp-asserts directory) |
398 | 431 | (let* ((default-directory directory) |
399 | (d (magit-diff--get-value 'magit-status-mode)) | |
400 | (l (magit-log--get-value 'magit-status-mode)) | |
432 | (d (magit-diff--get-value 'magit-status-mode | |
433 | magit-status-use-buffer-arguments)) | |
434 | (l (magit-log--get-value 'magit-status-mode | |
435 | magit-status-use-buffer-arguments)) | |
401 | 436 | (file (and magit-status-goto-file-position |
402 | 437 | (magit-file-relative-name))) |
403 | 438 | (line (and file (line-number-at-pos))) |
0 | 0 | ;;; magit-submodule.el --- submodule support for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2011-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2011-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
22 | 24 | |
23 | 25 | ;;; Code: |
24 | 26 | |
25 | (eval-when-compile | |
26 | (require 'subr-x)) | |
27 | ||
28 | 27 | (require 'magit) |
29 | 28 | |
30 | 29 | (defvar x-stretch-cursor) |
30 | ||
31 | 31 | ;;; Options |
32 | 32 | |
33 | 33 | (defcustom magit-module-sections-hook |
113 | 113 | ;;; Popup |
114 | 114 | |
115 | 115 | ;;;###autoload (autoload 'magit-submodule "magit-submodule" nil t) |
116 | (define-transient-command magit-submodule () | |
116 | (transient-define-prefix magit-submodule () | |
117 | 117 | "Act on a submodule." |
118 | 118 | :man-page "git-submodule" |
119 | 119 | ["Arguments" |
160 | 160 | (cl-call-next-method obj)))) |
161 | 161 | |
162 | 162 | ;;;###autoload (autoload 'magit-submodule-add "magit-submodule" nil t) |
163 | (define-suffix-command magit-submodule-add (url &optional path name args) | |
163 | (transient-define-suffix magit-submodule-add (url &optional path name args) | |
164 | 164 | "Add the repository at URL as a module. |
165 | 165 | |
166 | 166 | Optional PATH is the path to the module relative to the root of |
224 | 224 | (if prefer-short name path))))) |
225 | 225 | |
226 | 226 | ;;;###autoload (autoload 'magit-submodule-register "magit-submodule" nil t) |
227 | (define-suffix-command magit-submodule-register (modules) | |
227 | (transient-define-suffix magit-submodule-register (modules) | |
228 | 228 | "Register MODULES. |
229 | 229 | |
230 | 230 | With a prefix argument act on all suitable modules. Otherwise, |
243 | 243 | (magit-run-git-async "submodule" "init" "--" modules))) |
244 | 244 | |
245 | 245 | ;;;###autoload (autoload 'magit-submodule-populate "magit-submodule" nil t) |
246 | (define-suffix-command magit-submodule-populate (modules) | |
246 | (transient-define-suffix magit-submodule-populate (modules) | |
247 | 247 | "Create MODULES working directories, checking out the recorded commits. |
248 | 248 | |
249 | 249 | With a prefix argument act on all suitable modules. Otherwise, |
260 | 260 | (magit-run-git-async "submodule" "update" "--init" "--" modules))) |
261 | 261 | |
262 | 262 | ;;;###autoload (autoload 'magit-submodule-update "magit-submodule" nil t) |
263 | (define-suffix-command magit-submodule-update (modules args) | |
263 | (transient-define-suffix magit-submodule-update (modules args) | |
264 | 264 | "Update MODULES by checking out the recorded commits. |
265 | 265 | |
266 | 266 | With a prefix argument act on all suitable modules. Otherwise, |
283 | 283 | (magit-run-git-async "submodule" "update" args "--" modules))) |
284 | 284 | |
285 | 285 | ;;;###autoload (autoload 'magit-submodule-synchronize "magit-submodule" nil t) |
286 | (define-suffix-command magit-submodule-synchronize (modules args) | |
286 | (transient-define-suffix magit-submodule-synchronize (modules args) | |
287 | 287 | "Synchronize url configuration of MODULES. |
288 | 288 | |
289 | 289 | With a prefix argument act on all suitable modules. Otherwise, |
299 | 299 | (magit-run-git-async "submodule" "sync" args "--" modules))) |
300 | 300 | |
301 | 301 | ;;;###autoload (autoload 'magit-submodule-unpopulate "magit-submodule" nil t) |
302 | (define-suffix-command magit-submodule-unpopulate (modules args) | |
302 | (transient-define-suffix magit-submodule-unpopulate (modules args) | |
303 | 303 | "Remove working directories of MODULES. |
304 | 304 | |
305 | 305 | With a prefix argument act on all suitable modules. Otherwise, |
326 | 326 | "Unregister MODULES and remove their working directories. |
327 | 327 | |
328 | 328 | For safety reasons, do not remove the gitdirs and if a module has |
329 | uncomitted changes, then do not remove it at all. If a module's | |
329 | uncommitted changes, then do not remove it at all. If a module's | |
330 | 330 | gitdir is located inside the working directory, then move it into |
331 | 331 | the gitdir of the superproject first. |
332 | 332 |
0 | 0 | ;;; magit-subtree.el --- subtree support for Magit -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2011-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2011-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
27 | 29 | ;;; Commands |
28 | 30 | |
29 | 31 | ;;;###autoload (autoload 'magit-subtree "magit-subtree" nil t) |
30 | (define-transient-command magit-subtree () | |
32 | (transient-define-prefix magit-subtree () | |
31 | 33 | "Import or export subtrees." |
32 | 34 | :man-page "git-subtree" |
33 | 35 | ["Actions" |
35 | 37 | ("e" "Export" magit-subtree-export)]) |
36 | 38 | |
37 | 39 | ;;;###autoload (autoload 'magit-subtree-import "magit-subtree" nil t) |
38 | (define-transient-command magit-subtree-import () | |
40 | (transient-define-prefix magit-subtree-import () | |
39 | 41 | "Import subtrees." |
40 | 42 | :man-page "git-subtree" |
41 | 43 | ["Arguments" |
49 | 51 | ("f" "Pull" magit-subtree-pull)]]) |
50 | 52 | |
51 | 53 | ;;;###autoload (autoload 'magit-subtree-export "magit-subtree" nil t) |
52 | (define-transient-command magit-subtree-export () | |
54 | (transient-define-prefix magit-subtree-export () | |
53 | 55 | "Export subtrees." |
54 | 56 | :man-page "git-subtree" |
55 | 57 | ["Arguments" |
63 | 65 | ("p" "Push" magit-subtree-push) |
64 | 66 | ("s" "Split" magit-subtree-split)]) |
65 | 67 | |
66 | (define-infix-argument magit-subtree:--prefix () | |
68 | (transient-define-argument magit-subtree:--prefix () | |
67 | 69 | :description "Prefix" |
68 | 70 | :class 'transient-option |
69 | 71 | :shortarg "-P" |
81 | 83 | (user-error "%s isn't inside the repository at %s" prefix topdir)) |
82 | 84 | prefix))) |
83 | 85 | |
84 | (define-infix-argument magit-subtree:--message () | |
86 | (transient-define-argument magit-subtree:--message () | |
85 | 87 | :description "Message" |
86 | 88 | :class 'transient-option |
87 | 89 | :shortarg "-m" |
88 | 90 | :argument "--message=") |
89 | 91 | |
90 | (define-infix-argument magit-subtree:--annotate () | |
92 | (transient-define-argument magit-subtree:--annotate () | |
91 | 93 | :description "Annotate" |
92 | 94 | :class 'transient-option |
93 | 95 | :key "-a" |
94 | 96 | :argument "--annotate=") |
95 | 97 | |
96 | (define-infix-argument magit-subtree:--branch () | |
98 | (transient-define-argument magit-subtree:--branch () | |
97 | 99 | :description "Branch" |
98 | 100 | :class 'transient-option |
99 | 101 | :shortarg "-b" |
100 | 102 | :argument "--branch=") |
101 | 103 | |
102 | (define-infix-argument magit-subtree:--onto () | |
104 | (transient-define-argument magit-subtree:--onto () | |
103 | 105 | :description "Onto" |
104 | 106 | :class 'transient-option |
105 | 107 | :key "-o" |
0 | 0 | ;;; magit-tag.el --- tag functionality -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
28 | 30 | |
29 | 31 | (require 'magit) |
30 | 32 | |
33 | ;; For `magit-tag-delete'. | |
34 | (defvar helm-comp-read-use-marked) | |
35 | ||
31 | 36 | ;;;###autoload (autoload 'magit-tag "magit" nil t) |
32 | (define-transient-command magit-tag () | |
37 | (transient-define-prefix magit-tag () | |
33 | 38 | "Create or delete a tag." |
34 | 39 | :man-page "git-tag" |
35 | 40 | ["Arguments" |
47 | 52 | (defun magit-tag-arguments () |
48 | 53 | (transient-args 'magit-tag)) |
49 | 54 | |
50 | (define-infix-argument magit-tag:--local-user () | |
55 | (transient-define-argument magit-tag:--local-user () | |
51 | 56 | :description "Sign as" |
52 | 57 | :class 'transient-option |
53 | 58 | :shortarg "-u" |
54 | 59 | :argument "--local-user=" |
55 | :reader 'magit-read-gpg-secret-key | |
60 | :reader 'magit-read-gpg-signing-key | |
56 | 61 | :history-key 'magit:--gpg-sign) |
57 | 62 | |
58 | 63 | ;;;###autoload |
77 | 82 | \n(git tag -d TAGS)" |
78 | 83 | (interactive (list (--if-let (magit-region-values 'tag) |
79 | 84 | (magit-confirm t nil "Delete %i tags" nil it) |
80 | (magit-read-tag "Delete tag" t)))) | |
85 | (let ((helm-comp-read-use-marked t)) | |
86 | (magit-read-tag "Delete tag" t))))) | |
81 | 87 | (magit-run-git "tag" "-d" tags)) |
82 | 88 | |
83 | 89 | ;;;###autoload |
110 | 116 | (magit-run-git-async "push" remote (--map (concat ":" it) remote-tags)))) |
111 | 117 | |
112 | 118 | (defvar magit-tag-version-regexp-alist |
113 | '(("^[-._+ ]?alpha\\.?$" . -3) | |
119 | '(("^[-._+ ]?snapshot\\.?$" . -4) | |
120 | ("^[-._+]$" . -4) | |
121 | ("^[-._+ ]?\\(cvs\\|git\\|bzr\\|svn\\|hg\\|darcs\\)\\.?$" . -4) | |
122 | ("^[-._+ ]?unknown\\.?$" . -4) | |
123 | ("^[-._+ ]?alpha\\.?$" . -3) | |
114 | 124 | ("^[-._+ ]?beta\\.?$" . -2) |
115 | 125 | ("^[-._+ ]?\\(pre\\|rc\\)\\.?$" . -1)) |
116 | "Value to use for `version-regexp-alist' when parsing and sorting versions. | |
117 | The default value matches some common SemVer pre-release formats. | |
126 | "Overrides `version-regexp-alist' for `magit-tag-release'. | |
118 | 127 | See also `magit-release-tag-regexp'.") |
119 | 128 | |
120 | 129 | (defvar magit-release-tag-regexp "\\`\ |
121 | 130 | \\(?1:\\(?:v\\(?:ersion\\)?\\|r\\(?:elease\\)?\\)?[-_]?\\)?\ |
122 | 131 | \\(?2:[0-9]+\\(?:\\.[0-9]+\\)*\ |
123 | 132 | \\(?:-[a-zA-Z0-9-]+\\(?:\\.[a-zA-Z0-9-]+\\)*\\)?\\)\\'" |
124 | "Regexp used to parse release tag names. | |
125 | The first submatch must match the prefix, if any. | |
126 | The second submatch must match the version string. | |
127 | The default value matches SemVer version numbers, including | |
128 | pre-release versions. | |
129 | ||
130 | If this will match versions that are not dot separated numbers, you | |
131 | also need to set `magit-tag-version-regexp-alist' to recognize them | |
132 | and give them a sorting order.") | |
133 | ||
134 | ;;;###autoload | |
135 | (defun magit-tag-release (tag msg) | |
136 | "Create an annotated release tag. | |
133 | "Regexp used by `magit-tag-release' to parse release tags. | |
134 | ||
135 | The first submatch must match the prefix, if any. The second | |
136 | submatch must match the version string. | |
137 | ||
138 | If this matches versions that are not dot separated numbers, | |
139 | then `magit-tag-version-regexp-alist' has to contain entries | |
140 | for the separators allowed here.") | |
141 | ||
142 | ;;;###autoload | |
143 | (defun magit-tag-release (tag msg &optional args) | |
144 | "Create a release tag. | |
137 | 145 | |
138 | 146 | Assume that release tags match `magit-release-tag-regexp'. |
139 | 147 | |
141 | 149 | existing tag as initial input and leaving it to the user to |
142 | 150 | increment the desired part of the version string. |
143 | 151 | |
144 | Then prompt for the message of the new tag. Base the proposed | |
145 | tag message on the message of the highest tag, provided that | |
146 | that contains the corresponding version string and substituting | |
147 | the new version string for that. Otherwise propose something | |
148 | like \"Foo-Bar 1.2.3\", given, for example, a TAG \"v1.2.3\" and a | |
149 | repository located at something like \"/path/to/foo-bar\". | |
150 | ||
151 | Then call \"git tag --annotate --sign -m MSG TAG\" to create the, | |
152 | tag, regardless of whether these arguments are enabled in the | |
153 | popup. Finally show the refs buffer to let the user quickly | |
154 | review the result." | |
152 | If `--annotate' is enabled, then prompt for the message of the | |
153 | new tag. Base the proposed tag message on the message of the | |
154 | highest tag, provided that that contains the corresponding | |
155 | version string and substituting the new version string for that. | |
156 | Otherwise propose something like \"Foo-Bar 1.2.3\", given, for | |
157 | example, a TAG \"v1.2.3\" and a repository located at something | |
158 | like \"/path/to/foo-bar\"." | |
155 | 159 | (interactive |
156 | 160 | (save-match-data |
157 | 161 | (pcase-let* |
159 | 163 | (tag (read-string "Create release tag: " ptag)) |
160 | 164 | (ver (and (string-match magit-release-tag-regexp tag) |
161 | 165 | (match-string 2 tag))) |
162 | (msg (cond ((and pver (string-match (regexp-quote pver) pmsg)) | |
163 | (replace-match ver t t pmsg)) | |
164 | ((and ptag (string-match (regexp-quote ptag) pmsg)) | |
165 | (replace-match tag t t pmsg)) | |
166 | (t (format "%s %s" | |
167 | (capitalize | |
168 | (file-name-nondirectory | |
169 | (directory-file-name (magit-toplevel)))) | |
170 | ver))))) | |
171 | (list tag (read-string (format "Message for %S: " tag) msg))))) | |
172 | (magit-run-git-async "tag" "--annotate" "--sign" "-m" msg tag) | |
166 | (args (magit-tag-arguments))) | |
167 | (list tag | |
168 | (and (member "--annotate" args) | |
169 | (read-string | |
170 | (format "Message for %S: " tag) | |
171 | (cond ((and pver (string-match (regexp-quote pver) pmsg)) | |
172 | (replace-match ver t t pmsg)) | |
173 | ((and ptag (string-match (regexp-quote ptag) pmsg)) | |
174 | (replace-match tag t t pmsg)) | |
175 | (t (format "%s %s" | |
176 | (capitalize | |
177 | (file-name-nondirectory | |
178 | (directory-file-name (magit-toplevel)))) | |
179 | ver))))) | |
180 | args)))) | |
181 | (magit-run-git-async "tag" args (and msg (list "-m" msg)) tag) | |
173 | 182 | (set-process-sentinel |
174 | 183 | magit-this-process |
175 | 184 | (lambda (process event) |
0 | 0 | ;;; magit-transient.el --- support for transients -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
27 | 29 | |
28 | 30 | ;;; Code: |
29 | 31 | |
30 | (eval-when-compile | |
31 | (require 'subr-x)) | |
32 | ||
33 | (require 'transient) | |
34 | ||
35 | 32 | (require 'magit-git) |
36 | 33 | (require 'magit-mode) |
37 | 34 | (require 'magit-process) |
35 | ||
36 | (require 'transient) | |
38 | 37 | |
39 | 38 | ;;; Classes |
40 | 39 |
0 | 0 | ;;; magit-utils.el --- various utilities -*- lexical-binding: t; coding: utf-8 -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
9 | 9 | |
10 | 10 | ;; Contains code from GNU Emacs https://www.gnu.org/software/emacs, |
11 | 11 | ;; released under the GNU General Public License version 3 or later. |
12 | ||
13 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
12 | 14 | |
13 | 15 | ;; Magit is free software; you can redistribute it and/or modify it |
14 | 16 | ;; under the terms of the GNU General Public License as published by |
39 | 41 | |
40 | 42 | (require 'cl-lib) |
41 | 43 | (require 'dash) |
42 | ||
43 | (eval-when-compile | |
44 | (require 'subr-x)) | |
44 | (require 'eieio) | |
45 | (require 'seq) | |
46 | (require 'subr-x) | |
45 | 47 | |
46 | 48 | (require 'crm) |
47 | 49 | |
99 | 101 | (forge-browse-pullreq nil t) |
100 | 102 | (forge-edit-topic-title nil t) |
101 | 103 | (forge-edit-topic-state nil t) |
104 | (forge-edit-topic-milestone nil t) | |
102 | 105 | (forge-edit-topic-labels nil t) |
103 | 106 | (forge-edit-topic-marks nil t) |
104 | 107 | (forge-edit-topic-assignees nil t) |
105 | 108 | (forge-edit-topic-review-requests nil t) |
109 | (forge-edit-topic-note nil t) | |
106 | 110 | (forge-pull-pullreq nil t) |
107 | 111 | (forge-visit-issue nil t) |
108 | (forge-visit-pullreq nil t)) | |
112 | (forge-visit-pullreq nil t) | |
113 | (forge-visit-topic nil t)) | |
109 | 114 | "When not to offer alternatives and ask for confirmation. |
110 | 115 | |
111 | 116 | Many commands by default ask the user to select from a list of |
144 | 149 | (const :tag "use default without confirmation" t))))) |
145 | 150 | |
146 | 151 | (defconst magit--confirm-actions |
147 | '((const reverse) (const discard) | |
148 | (const rename) (const resurrect) | |
149 | (const untrack) (const trash) | |
150 | (const delete) (const abort-rebase) | |
151 | (const abort-merge) (const merge-dirty) | |
152 | (const drop-stashes) (const reset-bisect) | |
153 | (const kill-process) (const delete-unmerged-branch) | |
154 | (const delete-pr-branch) (const remove-modules) | |
155 | (const stage-all-changes) (const unstage-all-changes) | |
152 | '((const discard) | |
153 | (const reverse) | |
154 | (const stage-all-changes) | |
155 | (const unstage-all-changes) | |
156 | (const delete) | |
157 | (const trash) | |
158 | (const resurrect) | |
159 | (const untrack) | |
160 | (const rename) | |
161 | (const reset-bisect) | |
162 | (const abort-rebase) | |
163 | (const abort-merge) | |
164 | (const merge-dirty) | |
165 | (const delete-unmerged-branch) | |
166 | (const delete-branch-on-remote) | |
167 | (const delete-pr-remote) | |
168 | (const drop-stashes) | |
169 | (const set-and-push) | |
170 | (const amend-published) | |
171 | (const rebase-published) | |
172 | (const edit-published) | |
173 | (const remove-modules) | |
174 | (const remove-dirty-modules) | |
175 | (const trash-module-gitdirs) | |
176 | (const kill-process) | |
156 | 177 | (const safe-with-wip))) |
157 | 178 | |
158 | (defcustom magit-no-confirm nil | |
179 | (defcustom magit-no-confirm '(set-and-push) | |
159 | 180 | "A list of symbols for actions Magit should not confirm, or t. |
160 | 181 | |
161 | 182 | Many potentially dangerous commands by default ask the user for |
222 | 243 | to confirm the deletion of a branch by accepting the default |
223 | 244 | choice (or selecting another branch), but when a branch has |
224 | 245 | not been merged yet, also make sure the user is aware of that. |
246 | ||
247 | `delete-branch-on-remote' Deleting a \"remote branch\" may mean | |
248 | deleting the (local) \"remote-tracking\" branch only, or also | |
249 | removing it from the remote itself. The latter often makes more | |
250 | sense because otherwise simply fetching from the remote would | |
251 | restore the remote-tracking branch, but doing that can be | |
252 | surprising and hard to recover from, so we ask. | |
225 | 253 | |
226 | 254 | `delete-pr-remote' When deleting a branch that was created from |
227 | 255 | a pull-request and if no other branches still exist on that |
237 | 265 | to confirm by accepting the default (or selecting another). |
238 | 266 | This action only concerns the deletion of multiple stashes at |
239 | 267 | once. |
268 | ||
269 | Publishing: | |
270 | ||
271 | `set-and-push' When pushing to the upstream or the push-remote | |
272 | and that isn't actually configured yet, then the user can first | |
273 | set the target. If s/he confirms the default too quickly, then | |
274 | s/he might end up pushing to the wrong branch and if the remote | |
275 | repository is configured to disallow fixing such mistakes, then | |
276 | that can be quite embarrassing and annoying. | |
240 | 277 | |
241 | 278 | Edit published history: |
242 | 279 | |
352 | 389 | :group 'magit-miscellaneous |
353 | 390 | :type '(repeat string)) |
354 | 391 | |
355 | (defcustom magit-ellipsis ?… | |
356 | "Character used to abbreviate text. | |
357 | ||
358 | Currently this is used to abbreviate author names in the margin | |
359 | and in process buffers to elide `magit-git-global-arguments'." | |
360 | :package-version '(magit . "2.1.0") | |
392 | (defcustom magit-ellipsis (if (char-displayable-p ?…) "…" "...") | |
393 | "String used to abbreviate text in process buffers. | |
394 | ||
395 | Currently this is only used to elide `magit-git-global-arguments' | |
396 | in process buffers. In the future it may be used in other places | |
397 | as well, but not the following: | |
398 | ||
399 | - Author names in the log margin are always abbreviated using | |
400 | \"…\" or if that is not displayable, then \">\". | |
401 | ||
402 | - Whether collapsed sections are indicated using ellipsis is | |
403 | controlled by `magit-section-visibility-indicator'." | |
404 | :package-version '(magit . "3.0.0") | |
361 | 405 | :group 'magit-miscellaneous |
362 | :type 'character) | |
406 | :type 'string) | |
363 | 407 | |
364 | 408 | (defcustom magit-update-other-window-delay 0.2 |
365 | 409 | "Delay before automatically updating the other window. |
399 | 443 | ;;; User Input |
400 | 444 | |
401 | 445 | (defvar helm-completion-in-region-default-sort-fn) |
446 | (defvar helm-crm-default-separator) | |
402 | 447 | (defvar ivy-sort-functions-alist) |
448 | (defvar ivy-sort-matches-functions-alist) | |
403 | 449 | |
404 | 450 | (defvar magit-completing-read--silent-default nil) |
405 | 451 | |
473 | 519 | predicate |
474 | 520 | require-match initial-input hist def))) |
475 | 521 | (setq this-command command) |
476 | (if (string= reply "") | |
522 | ;; Note: Avoid `string=' to support `helm-comp-read-use-marked'. | |
523 | (if (equal reply "") | |
477 | 524 | (if require-match |
478 | 525 | (user-error "Nothing selected") |
479 | 526 | nil) |
520 | 567 | (minibuffer-completion-table #'crm--collection-fn) |
521 | 568 | (minibuffer-completion-confirm t) |
522 | 569 | (helm-completion-in-region-default-sort-fn nil) |
570 | (helm-crm-default-separator nil) | |
571 | (ivy-sort-matches-functions-alist nil) | |
523 | 572 | (input |
524 | 573 | (cl-letf (((symbol-function 'completion-pcm--all-completions) |
525 | 574 | #'magit-completion-pcm--all-completions)) |
554 | 603 | crm-local-must-match-map |
555 | 604 | crm-local-completion-map)) |
556 | 605 | (helm-completion-in-region-default-sort-fn nil) |
606 | (ivy-sort-matches-functions-alist nil) | |
557 | 607 | ;; If the user enters empty input, `read-from-minibuffer' |
558 | 608 | ;; returns the empty string, not DEF. |
559 | 609 | (input (read-from-minibuffer |
654 | 704 | (debug (form form &rest (characterp form body)))) |
655 | 705 | `(prog1 (pcase (read-char-choice |
656 | 706 | (concat ,prompt |
657 | ,(concat (mapconcat 'cadr clauses ", ") | |
658 | (and verbose ", or [C-g] to abort") " ")) | |
659 | ',(mapcar 'car clauses)) | |
707 | (mapconcat #'identity | |
708 | (list ,@(mapcar #'cadr clauses)) | |
709 | ", ") | |
710 | ,(if verbose ", or [C-g] to abort " " ")) | |
711 | ',(mapcar #'car clauses)) | |
660 | 712 | ,@(--map `(,(car it) ,@(cddr it)) clauses)) |
661 | 713 | (message ""))) |
662 | 714 | |
779 | 831 | (i 0)) |
780 | 832 | `(let ((,s ,string)) |
781 | 833 | (let ,(save-match-data |
782 | (--map (list it (list 'match-string (cl-incf i) s)) varlist)) | |
834 | (cl-mapcan (lambda (sym) | |
835 | (cl-incf i) | |
836 | (and (not (eq (aref (symbol-name sym) 0) ?_)) | |
837 | (list (list sym (list 'match-string i s))))) | |
838 | varlist)) | |
783 | 839 | ,@body)))) |
784 | 840 | |
785 | 841 | (defun magit-delete-line () |
812 | 868 | "Set the header-line using STRING. |
813 | 869 | Propertize STRING with the `magit-header-line'. If the `face' |
814 | 870 | property of any part of STRING is already set, then that takes |
815 | precedence. Also pad the left and right sides of STRING so that | |
816 | it aligns with the text area." | |
871 | precedence. Also pad the left side of STRING so that it aligns | |
872 | with the text area." | |
817 | 873 | (setq header-line-format |
818 | (concat | |
819 | (propertize " " 'display '(space :align-to 0)) | |
820 | string | |
821 | (propertize " " 'display | |
822 | `(space :width | |
823 | (+ left-fringe | |
824 | left-margin | |
825 | ,@(and (eq (car (window-current-scroll-bars)) | |
826 | 'left) | |
827 | '(scroll-bar))))))) | |
828 | (magit--add-face-text-property 0 (1- (length header-line-format)) | |
829 | 'magit-header-line t header-line-format)) | |
874 | (concat (propertize " " 'display '(space :align-to 0)) | |
875 | string))) | |
830 | 876 | |
831 | 877 | (defun magit-face-property-all (face string) |
832 | 878 | "Return non-nil if FACE is present in all of STRING." |
833 | (cl-loop for pos = 0 then (next-single-property-change | |
834 | pos 'font-lock-face string) | |
835 | unless pos | |
836 | return t | |
837 | for current = (get-text-property pos 'font-lock-face string) | |
838 | unless (if (consp current) | |
839 | (memq face current) | |
840 | (eq face current)) | |
841 | return nil)) | |
879 | (catch 'missing | |
880 | (let ((pos 0)) | |
881 | (while (setq pos (next-single-property-change pos 'font-lock-face string)) | |
882 | (let ((val (get-text-property pos 'font-lock-face string))) | |
883 | (unless (if (consp val) | |
884 | (memq face val) | |
885 | (eq face val)) | |
886 | (throw 'missing nil)))) | |
887 | (not pos)))) | |
842 | 888 | |
843 | 889 | (defun magit--add-face-text-property (beg end face &optional append object) |
844 | 890 | "Like `add-face-text-property' but for `font-lock-face'." |
845 | (cl-loop for pos = (next-single-property-change | |
846 | beg 'font-lock-face object end) | |
847 | for current = (get-text-property beg 'font-lock-face object) | |
848 | for newface = (if (listp current) | |
849 | (if append | |
850 | (append current (list face)) | |
851 | (cons face current)) | |
852 | (if append | |
853 | (list current face) | |
854 | (list face current))) | |
855 | do (progn (put-text-property beg pos 'font-lock-face newface object) | |
856 | (setq beg pos)) | |
857 | while (< beg end))) | |
891 | (while (< beg end) | |
892 | (let* ((pos (next-single-property-change beg 'font-lock-face object end)) | |
893 | (val (get-text-property beg 'font-lock-face object)) | |
894 | (val (if (listp val) val (list val)))) | |
895 | (put-text-property beg pos 'font-lock-face | |
896 | (if append | |
897 | (append val (list face)) | |
898 | (cons face val)) | |
899 | object) | |
900 | (setq beg pos)))) | |
858 | 901 | |
859 | 902 | (defun magit--propertize-face (string face) |
860 | 903 | (propertize string 'face face 'font-lock-face face)) |
1103 | 1146 | (advice-add 'org-man-export :around |
1104 | 1147 | 'org-man-export--magit-gitman) |
1105 | 1148 | |
1106 | ;;; Bitmaps | |
1107 | ||
1108 | (when (fboundp 'define-fringe-bitmap) | |
1109 | (define-fringe-bitmap 'magit-fringe-bitmap+ | |
1110 | [#b00000000 | |
1111 | #b00011000 | |
1112 | #b00011000 | |
1113 | #b01111110 | |
1114 | #b01111110 | |
1115 | #b00011000 | |
1116 | #b00011000 | |
1117 | #b00000000]) | |
1118 | (define-fringe-bitmap 'magit-fringe-bitmap- | |
1119 | [#b00000000 | |
1120 | #b00000000 | |
1121 | #b00000000 | |
1122 | #b01111110 | |
1123 | #b01111110 | |
1124 | #b00000000 | |
1125 | #b00000000 | |
1126 | #b00000000]) | |
1127 | ||
1128 | (define-fringe-bitmap 'magit-fringe-bitmap> | |
1129 | [#b01100000 | |
1130 | #b00110000 | |
1131 | #b00011000 | |
1132 | #b00001100 | |
1133 | #b00011000 | |
1134 | #b00110000 | |
1135 | #b01100000 | |
1136 | #b00000000]) | |
1137 | (define-fringe-bitmap 'magit-fringe-bitmapv | |
1138 | [#b00000000 | |
1139 | #b10000010 | |
1140 | #b11000110 | |
1141 | #b01101100 | |
1142 | #b00111000 | |
1143 | #b00010000 | |
1144 | #b00000000 | |
1145 | #b00000000]) | |
1146 | ||
1147 | (define-fringe-bitmap 'magit-fringe-bitmap-bold> | |
1148 | [#b11100000 | |
1149 | #b01110000 | |
1150 | #b00111000 | |
1151 | #b00011100 | |
1152 | #b00011100 | |
1153 | #b00111000 | |
1154 | #b01110000 | |
1155 | #b11100000]) | |
1156 | (define-fringe-bitmap 'magit-fringe-bitmap-boldv | |
1157 | [#b10000001 | |
1158 | #b11000011 | |
1159 | #b11100111 | |
1160 | #b01111110 | |
1161 | #b00111100 | |
1162 | #b00011000 | |
1163 | #b00000000 | |
1164 | #b00000000]) | |
1165 | ) | |
1149 | ;;; Kludges for Package Managers | |
1150 | ||
1151 | (defun magit--straight-chase-links (filename) | |
1152 | "Chase links in FILENAME until a name that is not a link. | |
1153 | ||
1154 | This is the same as `file-chase-links', except that it also | |
1155 | handles fake symlinks that are created by the package manager | |
1156 | straight.el on Windows. | |
1157 | ||
1158 | See <https://github.com/raxod502/straight.el/issues/520>." | |
1159 | (when (and (bound-and-true-p straight-symlink-emulation-mode) | |
1160 | (fboundp 'straight-chase-emulated-symlink)) | |
1161 | (when-let ((target (straight-chase-emulated-symlink filename))) | |
1162 | (unless (eq target 'broken) | |
1163 | (setq filename target)))) | |
1164 | (file-chase-links filename)) | |
1166 | 1165 | |
1167 | 1166 | ;;; Miscellaneous |
1168 | 1167 | |
1186 | 1185 | (save-excursion |
1187 | 1186 | (save-restriction |
1188 | 1187 | (widen) |
1189 | (goto-char ,pos) | |
1188 | (goto-char (or ,pos 1)) | |
1190 | 1189 | ,@body)))) |
1191 | 1190 | |
1192 | 1191 | ;;; _ |
0 | 0 | ;;; magit-wip.el --- commit snapshots to work-in-progress refs -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
29 | 31 | |
30 | 32 | ;;; Code: |
31 | 33 | |
32 | (eval-when-compile | |
33 | (require 'subr-x)) | |
34 | ||
35 | 34 | (require 'magit-core) |
36 | 35 | (require 'magit-log) |
37 | 36 | |
106 | 105 | |
107 | 106 | ;;; Modes |
108 | 107 | |
108 | ;;;###autoload | |
109 | 109 | (define-minor-mode magit-wip-mode |
110 | 110 | "Save uncommitted changes to work-in-progress refs. |
111 | 111 |
0 | 0 | ;;; magit-worktree.el --- worktree support -*- lexical-binding: t -*- |
1 | 1 | |
2 | ;; Copyright (C) 2010-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2010-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Jonas Bernoulli <jonas@bernoul.li> |
8 | 8 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ||
10 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
9 | 11 | |
10 | 12 | ;; Magit is free software; you can redistribute it and/or modify it |
11 | 13 | ;; under the terms of the GNU General Public License as published by |
42 | 44 | ;;; Commands |
43 | 45 | |
44 | 46 | ;;;###autoload (autoload 'magit-worktree "magit-worktree" nil t) |
45 | (define-transient-command magit-worktree () | |
47 | (transient-define-prefix magit-worktree () | |
46 | 48 | "Act on a worktree." |
47 | 49 | :man-page "git-worktree" |
48 | 50 | [["Create new" |
161 | 163 | (pcase-lambda (`(,path ,barep ,commit ,branch)) |
162 | 164 | (cons (cond |
163 | 165 | (branch (propertize |
164 | branch 'font-lock-face 'magit-branch-local)) | |
166 | branch 'font-lock-face | |
167 | (if (equal branch (magit-get-current-branch)) | |
168 | 'magit-branch-current | |
169 | 'magit-branch-local))) | |
165 | 170 | (commit (propertize (magit-rev-abbrev commit) |
166 | 171 | 'font-lock-face 'magit-hash)) |
167 | 172 | (barep "(bare)")) |
171 | 176 | (pcase-dolist (`(,head . ,path) cols) |
172 | 177 | (magit-insert-section (worktree path) |
173 | 178 | (insert head) |
174 | (indent-to align) | |
179 | (insert (make-string (- align (length head)) ?\s)) | |
175 | 180 | (insert (let ((r (file-relative-name path)) |
176 | 181 | (a (abbreviate-file-name path))) |
177 | 182 | (if (< (string-width r) (string-width a)) r a))) |
0 | 0 | ;;; magit.el --- A Git porcelain inside Emacs -*- lexical-binding: t; coding: utf-8 -*- |
1 | 1 | |
2 | ;; Copyright (C) 2008-2020 The Magit Project Contributors | |
2 | ;; Copyright (C) 2008-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; You should have received a copy of the AUTHORS.md file which |
5 | 5 | ;; lists all contributors. If not, see http://magit.vc/authors. |
6 | 6 | |
7 | 7 | ;; Author: Marius Vollmer <marius.vollmer@gmail.com> |
8 | ;; Jonas Bernoulli <jonas@bernoul.li> | |
8 | 9 | ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> |
9 | ;; Kyle Meyer <kyle@kyleam.com> | |
10 | ;; Noam Postavsky <npostavs@users.sourceforge.net> | |
10 | ;; Kyle Meyer <kyle@kyleam.com> | |
11 | ;; Noam Postavsky <npostavs@users.sourceforge.net> | |
11 | 12 | ;; Former-Maintainers: |
12 | ;; Nicolas Dudebout <nicolas.dudebout@gatech.edu> | |
13 | ;; Peter J. Weisberg <pj@irregularexpressions.net> | |
14 | ;; Phil Jackson <phil@shellarchive.co.uk> | |
15 | ;; Rémi Vanicat <vanicat@debian.org> | |
16 | ;; Yann Hodique <yann.hodique@gmail.com> | |
13 | ;; Nicolas Dudebout <nicolas.dudebout@gatech.edu> | |
14 | ;; Peter J. Weisberg <pj@irregularexpressions.net> | |
15 | ;; Phil Jackson <phil@shellarchive.co.uk> | |
16 | ;; Rémi Vanicat <vanicat@debian.org> | |
17 | ;; Yann Hodique <yann.hodique@gmail.com> | |
17 | 18 | |
18 | 19 | ;; Keywords: git tools vc |
19 | 20 | ;; Homepage: https://github.com/magit/magit |
20 | ||
21 | ;; Magit requires at least GNU Emacs 25.1 and Git 2.2.0. | |
21 | ;; SPDX-License-Identifier: GPL-3.0-or-later | |
22 | 22 | |
23 | 23 | ;; Magit is free software; you can redistribute it and/or modify it |
24 | 24 | ;; under the terms of the GNU General Public License as published by |
33 | 33 | ;; You should have received a copy of the GNU General Public License |
34 | 34 | ;; along with Magit. If not, see http://www.gnu.org/licenses. |
35 | 35 | |
36 | ;; Magit requires at least GNU Emacs 25.1 and Git 2.2.0. | |
37 | ||
36 | 38 | ;;; Commentary: |
37 | 39 | |
38 | ;; Magit is an interface to the version control system Git, | |
39 | ;; implemented as an Emacs package. Magit aspires to be a complete | |
40 | ;; Git porcelain. While we cannot (yet) claim, that Magit wraps and | |
41 | ;; improves upon each and every Git command, it is complete enough to | |
42 | ;; allow even experienced Git users to perform almost all of their | |
43 | ;; daily version control tasks directly from within Emacs. While many | |
44 | ;; fine Git clients exist, only Magit and Git itself deserve to be | |
45 | ;; called porcelains. | |
40 | ;; Magit is a text-based Git user interface that puts an unmatched focus | |
41 | ;; on streamlining workflows. Commands are invoked using short mnemonic | |
42 | ;; key sequences that take the cursor’s position in the highly actionable | |
43 | ;; interface into account to provide context-sensitive behavior. | |
44 | ||
45 | ;; With Magit you can do nearly everything that you can do when using Git | |
46 | ;; on the command-line, but at greater speed and while taking advantage | |
47 | ;; of advanced features that previously seemed too daunting to use on a | |
48 | ;; daily basis. Many users will find that by using Magit they can become | |
49 | ;; more effective Git user. | |
46 | 50 | |
47 | 51 | ;;; Code: |
48 | 52 | |
49 | (require 'cl-lib) | |
50 | (require 'dash) | |
51 | ||
52 | (require 'subr-x) | |
53 | ||
54 | (require 'with-editor) | |
55 | (require 'git-commit) | |
56 | 53 | (require 'magit-core) |
57 | 54 | (require 'magit-diff) |
58 | 55 | (require 'magit-log) |
59 | 56 | (require 'magit-wip) |
60 | 57 | (require 'magit-apply) |
61 | 58 | (require 'magit-repos) |
59 | (require 'git-commit) | |
62 | 60 | |
63 | 61 | (require 'format-spec) |
64 | 62 | (require 'package nil t) ; used in `magit-version' |
63 | (require 'with-editor) | |
65 | 64 | |
66 | 65 | (defconst magit--minimal-git "2.2.0") |
67 | 66 | (defconst magit--minimal-emacs "25.1") |
179 | 178 | :group 'magit-faces) |
180 | 179 | |
181 | 180 | (defface magit-signature-untrusted |
182 | '((t :foreground "cyan")) | |
181 | '((t :foreground "medium aquamarine")) | |
183 | 182 | "Face for good untrusted signatures." |
184 | 183 | :group 'magit-faces) |
185 | 184 | |
199 | 198 | :group 'magit-faces) |
200 | 199 | |
201 | 200 | (defface magit-signature-error |
202 | '((t :foreground "firebrick3")) | |
201 | '((t :foreground "light blue")) | |
203 | 202 | "Face for signatures that cannot be checked (e.g. missing key)." |
204 | 203 | :group 'magit-faces) |
205 | 204 | |
218 | 217 | "Face for filenames." |
219 | 218 | :group 'magit-faces) |
220 | 219 | |
220 | ;;; Global Bindings | |
221 | ||
222 | ;;;###autoload | |
223 | (define-obsolete-variable-alias 'global-magit-file-mode | |
224 | 'magit-define-global-key-bindings "Magit 3.0.0") | |
225 | ||
226 | ;;;###autoload | |
227 | (defcustom magit-define-global-key-bindings t | |
228 | "Whether to bind some Magit commands in the global keymap. | |
229 | ||
230 | If this variable is non-nil, then the following bindings may | |
231 | be added to the global keymap. The default is t. | |
232 | ||
233 | key binding | |
234 | --- ------- | |
235 | C-x g magit-status | |
236 | C-x M-g magit-dispatch | |
237 | C-c M-g magit-file-dispatch | |
238 | ||
239 | These bindings may be added when `after-init-hook' is called. | |
240 | Each binding is added if and only if at that time no other key | |
241 | is bound to the same command and no other command is bound to | |
242 | the same key. In other words we try to avoid adding bindings | |
243 | that are unnecessary, as well as bindings that conflict with | |
244 | other bindings. | |
245 | ||
246 | Adding the above bindings is delayed until `after-init-hook' | |
247 | is called to allow users to set the variable anywhere in their | |
248 | init file (without having to make sure to do so before `magit' | |
249 | is loaded or autoloaded) and to increase the likelihood that | |
250 | all the potentially conflicting user bindings have already | |
251 | been added. | |
252 | ||
253 | Setting this variable after the hook has already been called | |
254 | has no effect. | |
255 | ||
256 | We recommend that you bind \"C-c g\" instead of \"C-c M-g\" to | |
257 | `magit-file-dispatch'. The former is a much better binding | |
258 | but the \"C-c <letter>\" namespace is strictly reserved for | |
259 | users; preventing Magit from using it by default. | |
260 | ||
261 | Also see info node `(magit)Commands for Buffers Visiting Files'." | |
262 | :package-version '(magit . "3.0.0") | |
263 | :group 'magit-essentials | |
264 | :type 'boolean) | |
265 | ||
266 | ;;;###autoload | |
267 | (progn | |
268 | (defun magit-maybe-define-global-key-bindings () | |
269 | (when magit-define-global-key-bindings | |
270 | (let ((map (current-global-map))) | |
271 | (dolist (elt '(("C-x g" . magit-status) | |
272 | ("C-x M-g" . magit-dispatch) | |
273 | ("C-c M-g" . magit-file-dispatch))) | |
274 | (let ((key (kbd (car elt))) | |
275 | (def (cdr elt))) | |
276 | (unless (or (lookup-key map key) | |
277 | (where-is-internal def (make-sparse-keymap) t)) | |
278 | (define-key map key def))))))) | |
279 | (if after-init-time | |
280 | (magit-maybe-define-global-key-bindings) | |
281 | (add-hook 'after-init-hook 'magit-maybe-define-global-key-bindings t))) | |
282 | ||
221 | 283 | ;;; Dispatch Popup |
222 | 284 | |
223 | 285 | ;;;###autoload (autoload 'magit-dispatch "magit" nil t) |
224 | (define-transient-command magit-dispatch () | |
286 | (transient-define-prefix magit-dispatch () | |
225 | 287 | "Invoke a Magit command from a list of available commands." |
288 | :info-manual "(magit)Top" | |
226 | 289 | ["Transient and dwim commands" |
227 | 290 | [("A" "Apply" magit-cherry-pick) |
228 | 291 | ("b" "Branch" magit-branch) |
235 | 298 | ("E" "Ediff" magit-ediff)] |
236 | 299 | [("f" "Fetch" magit-fetch) |
237 | 300 | ("F" "Pull" magit-pull) |
301 | ("I" "Init" magit-init) | |
238 | 302 | ("l" "Log" magit-log) |
239 | 303 | ("L" "Log (change)" magit-log-refresh) |
240 | 304 | ("m" "Merge" magit-merge) |
283 | 347 | (defvar magit-git-command-history nil) |
284 | 348 | |
285 | 349 | ;;;###autoload (autoload 'magit-run "magit" nil t) |
286 | (define-transient-command magit-run () | |
350 | (transient-define-prefix magit-run () | |
287 | 351 | "Run git or another command, or launch a graphical utility." |
352 | ||
353 | ||
288 | 354 | [["Run git subcommand" |
289 | 355 | ("!" "in repository root" magit-git-command-topdir) |
290 | 356 | ("p" "in working directory" magit-git-command)] |
349 | 415 | (magit-process-buffer)) |
350 | 416 | |
351 | 417 | (defun magit-read-shell-command (&optional toplevel initial-input) |
352 | (let ((dir (abbreviate-file-name | |
353 | (if (or toplevel current-prefix-arg) | |
354 | (or (magit-toplevel) | |
355 | (magit--not-inside-repository-error)) | |
356 | default-directory)))) | |
418 | (let ((default-directory | |
419 | (if (or toplevel current-prefix-arg) | |
420 | (or (magit-toplevel) | |
421 | (magit--not-inside-repository-error)) | |
422 | default-directory))) | |
357 | 423 | (read-shell-command (if magit-shell-command-verbose-prompt |
358 | (format "Async shell command in %s: " dir) | |
424 | (format "Async shell command in %s: " | |
425 | (abbreviate-file-name default-directory)) | |
359 | 426 | "Async shell command: ") |
360 | 427 | initial-input 'magit-git-command-history))) |
361 | 428 | |
399 | 466 | (unless (and toplib |
400 | 467 | (equal (file-name-nondirectory toplib) "magit.el")) |
401 | 468 | (setq toplib (locate-library "magit.el"))) |
402 | (setq toplib (and toplib (file-chase-links toplib))) | |
469 | (setq toplib (and toplib (magit--straight-chase-links toplib))) | |
403 | 470 | (push toplib debug) |
404 | 471 | (when toplib |
405 | 472 | (let* ((topdir (file-name-directory toplib)) |
407 | 474 | ".git" (file-name-directory |
408 | 475 | (directory-file-name topdir)))) |
409 | 476 | (static (locate-library "magit-version.el" nil (list topdir))) |
410 | (static (and static (file-chase-links static)))) | |
477 | (static (and static (magit--straight-chase-links static)))) | |
411 | 478 | (or (progn |
412 | 479 | (push 'repo debug) |
413 | 480 | (when (and (file-exists-p gitdir) |
421 | 488 | (ignore-errors (delete-file static))) |
422 | 489 | (setq magit-version |
423 | 490 | (let ((default-directory topdir)) |
424 | (magit-git-string "describe" "--tags" "--dirty"))))) | |
491 | (magit-git-string "describe" | |
492 | "--tags" "--dirty" "--always"))))) | |
425 | 493 | (progn |
426 | 494 | (push 'static debug) |
427 | 495 | (when (and static (file-exists-p static)) |
441 | 509 | (push 'dirname debug) |
442 | 510 | (let ((dirname (file-name-nondirectory |
443 | 511 | (directory-file-name topdir)))) |
444 | (when (string-match "\\`magit-\\([0-9]\\{8\\}\\.[0-9]*\\)" | |
445 | dirname) | |
446 | (setq magit-version (match-string 1 dirname)))))))) | |
512 | (when (string-match "\\`magit-\\([0-9].*\\)" dirname) | |
513 | (setq magit-version (match-string 1 dirname))))) | |
514 | ;; If all else fails, just report the commit hash. It's | |
515 | ;; better than nothing and we cannot do better in the case | |
516 | ;; of e.g. a shallow clone. | |
517 | (progn | |
518 | (push 'hash debug) | |
519 | ;; Same check as above to see if it's really the Magit repo. | |
520 | (when (and (file-exists-p gitdir) | |
521 | (file-exists-p | |
522 | (expand-file-name "../lisp/magit.el" gitdir))) | |
523 | (setq magit-version | |
524 | (let ((default-directory topdir)) | |
525 | (magit-git-string "rev-parse" "HEAD")))))))) | |
447 | 526 | (if (stringp magit-version) |
448 | 527 | (when print-dest |
449 | 528 | (princ (format "Magit %s, Git %s, Emacs %s, %s" |
461 | 540 | (setq magit-version 'error) |
462 | 541 | (when magit-version |
463 | 542 | (push magit-version debug)) |
464 | (unless (equal (getenv "TRAVIS") "true") | |
543 | (unless (equal (getenv "CI") "true") | |
465 | 544 | ;; The repository is a sparse clone. |
466 | 545 | (message "Cannot determine Magit's version %S" debug))) |
467 | 546 | magit-version)) |
516 | 595 | (let ((version (magit-git-version))) |
517 | 596 | (when (and version |
518 | 597 | (version< version magit--minimal-git) |
519 | (not (equal (getenv "TRAVIS") "true"))) | |
598 | (not (equal (getenv "CI") "true"))) | |
520 | 599 | (display-warning 'magit (format "\ |
521 | 600 | Magit requires Git >= %s, you are using %s. |
522 | 601 | |
583 | 662 | (require 'magit-imenu) |
584 | 663 | (require 'magit-bookmark))) |
585 | 664 | |
586 | (eval-after-load 'bookmark | |
587 | '(require 'magit-bookmark)) | |
665 | (with-eval-after-load 'bookmark | |
666 | (require 'magit-bookmark)) | |
588 | 667 | |
589 | 668 | (if after-init-time |
590 | 669 | (progn (magit-startup-asserts) |
0 | let | |
1 | emacs-overlay = import (builtins.fetchTarball { url = https://github.com/nix-community/emacs-overlay/archive/master.tar.gz; }); | |
2 | emacs-ci = import (builtins.fetchTarball { url = https://github.com/purcell/nix-emacs-ci/archive/master.tar.gz; }); | |
3 | ||
4 | pkgs = import <nixpkgs> { overlays = [ emacs-overlay ]; }; | |
5 | in | |
6 | builtins.mapAttrs | |
7 | (version: emacs: | |
8 | (pkgs.emacsPackagesGen emacs).emacsWithPackages | |
9 | (emacsPackages: [ | |
10 | emacsPackages.dash | |
11 | emacsPackages.transient | |
12 | ] ++ (with emacsPackages.melpaPackages; [ | |
13 | libgit | |
14 | with-editor | |
15 | ]) | |
16 | )) | |
17 | emacs-ci |
0 | 0 | ;;; magit-tests.el --- tests for Magit |
1 | 1 | |
2 | ;; Copyright (C) 2011-2018 The Magit Project Contributors | |
2 | ;; Copyright (C) 2011-2021 The Magit Project Contributors | |
3 | 3 | ;; |
4 | 4 | ;; License: GPLv3 |
5 | 5 | |
12 | 12 | (require 'tramp-sh) |
13 | 13 | |
14 | 14 | (require 'magit) |
15 | ||
16 | (defun magit-test-init-repo (dir &rest args) | |
17 | (let ((magit-git-global-arguments | |
18 | (nconc (list "-c" "init.defaultBranch=master") | |
19 | magit-git-global-arguments))) | |
20 | (magit-git "init" args dir))) | |
15 | 21 | |
16 | 22 | (defmacro magit-with-test-directory (&rest body) |
17 | 23 | (declare (indent 0) (debug t)) |
22 | 28 | (push "GIT_AUTHOR_EMAIL=a.u.thor@example.com" process-environment) |
23 | 29 | (condition-case err |
24 | 30 | (cl-letf (((symbol-function #'message) (lambda (&rest _)))) |
25 | (let ((default-directory ,dir)) | |
31 | (let ((default-directory (file-truename ,dir))) | |
26 | 32 | ,@body)) |
27 | 33 | (error (message "Keeping test directory:\n %s" ,dir) |
28 | 34 | (signal (car err) (cdr err)))) |
30 | 36 | |
31 | 37 | (defmacro magit-with-test-repository (&rest body) |
32 | 38 | (declare (indent 0) (debug t)) |
33 | `(magit-with-test-directory (magit-git "init" ".") ,@body)) | |
39 | `(magit-with-test-directory (magit-test-init-repo ".") ,@body)) | |
40 | ||
41 | (defmacro magit-with-bare-test-repository (&rest body) | |
42 | (declare (indent 1) (debug t)) | |
43 | `(magit-with-test-directory (magit-test-init-repo "." "--bare") ,@body)) | |
34 | 44 | |
35 | 45 | ;;; Git |
36 | 46 | |
45 | 55 | (ert-deftest magit-toplevel:basic () |
46 | 56 | (let ((find-file-visit-truename nil)) |
47 | 57 | (magit-with-test-directory |
48 | (magit-git "init" "repo") | |
58 | (magit-test-init-repo "repo") | |
49 | 59 | (magit-test-magit-toplevel) |
50 | 60 | (should (equal (magit-toplevel "repo/.git/") |
51 | 61 | (expand-file-name "repo/"))) |
67 | 77 | ;; require a functioning `sudo'. |
68 | 78 | (sudo-method (cdr (assoc "sudo" tramp-methods))) |
69 | 79 | ((cdr (assq 'tramp-login-program sudo-method)) |
70 | (list shell-file-name)) | |
80 | (list (if (file-executable-p "/bin/sh") | |
81 | "/bin/sh" | |
82 | shell-file-name))) | |
71 | 83 | ((cdr (assq 'tramp-login-args sudo-method)) nil)) |
72 | 84 | (magit-with-test-directory |
73 | 85 | (setq default-directory |
74 | 86 | (concat (format "/sudo:%s@localhost:" (user-login-name)) |
75 | 87 | default-directory)) |
76 | (magit-git "init" "repo") | |
88 | (magit-test-init-repo "repo") | |
77 | 89 | (magit-test-magit-toplevel) |
78 | 90 | (should (equal (magit-toplevel "repo/.git/") |
79 | 91 | (expand-file-name "repo/"))) |
87 | 99 | (ert-deftest magit-toplevel:submodule () |
88 | 100 | (let ((find-file-visit-truename nil)) |
89 | 101 | (magit-with-test-directory |
90 | (magit-git "init" "remote") | |
102 | (magit-test-init-repo "remote") | |
91 | 103 | (let ((default-directory (expand-file-name "remote/"))) |
92 | 104 | (magit-git "commit" "-m" "init" "--allow-empty")) |
93 | (magit-git "init" "super") | |
105 | (magit-test-init-repo "super") | |
94 | 106 | (setq default-directory (expand-file-name "super/")) |
95 | 107 | (magit-git "submodule" "add" "../remote" "repo/") |
96 | 108 | (magit-test-magit-toplevel) |
132 | 144 | (should (equal (magit-toplevel "subdir-link-indirect") |
133 | 145 | (expand-file-name "repo/"))) |
134 | 146 | ;; wrap/*link |
135 | (magit-git "init" "wrap") | |
147 | (magit-test-init-repo "wrap") | |
136 | 148 | (make-symbolic-link "../repo" "wrap/repo-link") |
137 | 149 | (make-symbolic-link "../repo/subdir" "wrap/subdir-link") |
138 | 150 | (make-symbolic-link "../repo/subdir/subsubdir" "wrap/subsubdir-link") |
153 | 165 | |
154 | 166 | (ert-deftest magit-get () |
155 | 167 | (magit-with-test-directory |
156 | (magit-git "init" "remote") | |
168 | (magit-test-init-repo "remote") | |
157 | 169 | (let ((default-directory (expand-file-name "remote/"))) |
158 | 170 | (magit-git "commit" "-m" "init" "--allow-empty") |
159 | 171 | (magit-git "config" "a.b" "remote-value")) |
160 | (magit-git "init" "super") | |
172 | (magit-test-init-repo "super") | |
161 | 173 | (setq default-directory (expand-file-name "super/")) |
162 | 174 | ;; Some tricky cases: |
163 | 175 | ;; Multiple config values. |
323 | 335 | '(unpushed . "@{upstream}..") |
324 | 336 | (magit-rev-parse "--short" "master"))))) |
325 | 337 | |
338 | ;;; libgit | |
339 | ||
340 | (ert-deftest magit-in-bare-repo () | |
341 | "Test `magit-bare-repo-p' in a bare repository." | |
342 | (magit-with-bare-test-repository | |
343 | (should (magit-bare-repo-p)))) | |
344 | ||
345 | (ert-deftest magit-in-non-bare-repo () | |
346 | "Test `magit-bare-repo-p' in a non-bare repository." | |
347 | (magit-with-test-repository | |
348 | (should-not (magit-bare-repo-p)))) | |
349 | ||
326 | 350 | ;;; Utils |
327 | 351 | |
328 | 352 | (ert-deftest magit-utils:add-face-text-property () |