Compare commits

...

687 commits

Author SHA1 Message Date
leo
190d2eec73
Merge branch 'release/v2025.23' 2025-06-23 09:59:54 +08:00
leo
bfb9d6b6bc
version: Release 2025.23
Signed-off-by: leo <longshuang@msn.cn>
2025-06-23 09:59:45 +08:00
leo
9d2f8b1555
fix: Conventional Commit Helper not working in Interactive Rebase (#1446)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-23 09:56:18 +08:00
github-actions[bot]
f59b34fe25 doc: Update translation status and sort locale files
Some checks failed
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
2025-06-22 01:30:06 +00:00
Sina Hinderks
6e4f35c4e1
localization: update German translations (#1445) 2025-06-22 09:29:54 +08:00
github-actions[bot]
6ecdabc212 doc: Update translation status and sort locale files 2025-06-22 01:29:17 +00:00
AquariusStar
8b902bd5c9
localization: update russian translate (#1444) 2025-06-22 09:28:58 +08:00
github-actions[bot]
73eccdb495 doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
2025-06-21 04:03:36 +00:00
leo
9bfc315ace
feature: allow to push revision where local branch is ahead of its upstream (#1394) (#1441)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-21 11:58:56 +08:00
leo
64ffbb113f
fix: wrong localization key to compare remote branch with current
Signed-off-by: leo <longshuang@msn.cn>
2025-06-21 10:45:40 +08:00
leo
221e964df0
code_review: PR #1442
Trim both `\` (on Windows) and `/` (on Linux/macOS) characters at the end of path

Signed-off-by: leo <longshuang@msn.cn>
2025-06-21 10:42:19 +08:00
Ihor
20daa584e3
fix: fix working tree folder path detection error (#1442)
Fix the error of adding an extra slash when selecting a working tree directory.
2025-06-21 10:25:55 +08:00
leo
c5ad4b837d
feature: auto-follow HEAD when bisecting (#1438)
Some checks failed
Continuous Integration / Build (push) Has been cancelled
Continuous Integration / Prepare version string (push) Has been cancelled
Continuous Integration / Package (push) Has been cancelled
Signed-off-by: leo <longshuang@msn.cn>
2025-06-20 09:19:21 +08:00
leo
957c52aac4
fix: crash after remove worktree while it is opened in sourcegit (#1436)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-19 17:58:02 +08:00
leo
8d74586970
ux: show only subject in Apply Stash and Drop Stash popup
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-19 14:26:31 +08:00
leo
dcd8effc32
fix: saving revision file may crash this app if target dir is not exists (#1434)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-19 11:39:53 +08:00
leo
af2b644792
code_review: PR #1416
- Split `DoubleTapped` into two methods: `CheckoutBranchByDecorator` and `CheckoutBranchByCommit`
- Move `DoubleTappedEvent` from whole ListBox to the row tapped actually
- Do nothing if the decorator double-clicked is HEAD
- Code-style

Signed-off-by: leo <longshuang@msn.cn>
2025-06-19 11:31:04 +08:00
Nathan Baulch
88fd8f32f1
feature: double tap specific branch (#1416)
* feature: double tap specific branch
* exactly match behavior of left sidebar
2025-06-19 10:27:31 +08:00
leo
cadcf40d74
feature: support to open selected folder in file manager
Signed-off-by: leo <longshuang@msn.cn>
2025-06-19 09:32:27 +08:00
github-actions[bot]
fcf1107304 doc: Update translation status and sort locale files
Some checks failed
Localization Check / localization-check (push) Has been cancelled
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
2025-06-19 01:13:09 +00:00
leo
e81674912c
refactor: remove duplicated context menu to ignore untracked files under folder
Signed-off-by: leo <longshuang@msn.cn>
2025-06-19 09:12:27 +08:00
leo
6729d4e896
feature: supports to ignore new files in folder from context menu of selected folder node in change tree (#1432)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 22:10:23 +08:00
leo
f9f44ae9cb
ux: show Name of stash instead of SHA in Apply Stash and Drop Stash popup
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 20:33:49 +08:00
leo
c67e8e3c64
enhance: create only one filesystem watcher when repo's $GIT_DIR is the same as $REPO_ROOT/.git
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 17:12:50 +08:00
leo
5ec8ae1296
ux: use CheckCircled instead of Check icon for stash apply context menu
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 14:37:14 +08:00
leo
2d91fed05e
code_style: remove unnecessary namespace using
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 14:30:46 +08:00
github-actions[bot]
3ac803d88c doc: Update translation status and sort locale files 2025-06-18 05:55:24 +00:00
leo
94d25ee6c9
code_review: PR #1430
- add missing Chinese translations
- add missing icons for stash context menu

Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 13:55:03 +08:00
github-actions[bot]
3711399c59 doc: Update translation status and sort locale files 2025-06-18 05:48:50 +00:00
Göran W
004022648c
Add "Copy Message" to context-menu for Stash item (#1430)
* Refactor: Simplify parsing in QueryStashes, by passing the `-z` argument to `git stash list` for item separation.
* Add "Copy Message" command in stash-item context-menu.
2025-06-18 13:48:39 +08:00
leo
240db2ea2f
ux: use small font size for tips
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 12:51:39 +08:00
github-actions[bot]
5ca1fcfd8f doc: Update translation status and sort locale files 2025-06-18 04:37:53 +00:00
leo
bad8904edc
code_style: make sure translations are ordered by key
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 12:37:39 +08:00
github-actions[bot]
86828b9711 doc: Update translation status and sort locale files 2025-06-18 04:33:05 +00:00
leo
6d682ac409
refactor: stash (#1420) (#1426)
- supports to use multi-line as stash message
- new style to display stashes

Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 12:32:50 +08:00
leo
b06a4cbb8a
code_style: remove whitespaces
Signed-off-by: leo <longshuang@msn.cn>
2025-06-18 09:32:39 +08:00
Nathan Baulch
d404f6dbe2
code_style: general cleanup (#1428) 2025-06-18 09:29:18 +08:00
github-actions[bot]
ae46728bbc doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
2025-06-17 12:56:18 +00:00
leo
957fbbf54f
refactor: rewrite stash local changes
Signed-off-by: leo <longshuang@msn.cn>
2025-06-17 20:56:02 +08:00
github-actions[bot]
10569022d7 doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Continuous Integration / Build (push) Waiting to run
Localization Check / localization-check (push) Waiting to run
2025-06-17 07:08:25 +00:00
leo
90310a704d
feature: supports to customize merge message (--edit) (#1421)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-17 15:07:55 +08:00
leo
a8da8c09ac
feature: add a button to open current revision file with default editor in FileHistories
Signed-off-by: leo <longshuang@msn.cn>
2025-06-17 14:44:12 +08:00
leo
df7375313e
enhance: clear last view revision file info after commit changed
Signed-off-by: leo <longshuang@msn.cn>
2025-06-17 14:12:00 +08:00
leo
efa6e46471
feature: add a button to open current revision file with default editor
Signed-off-by: leo <longshuang@msn.cn>
2025-06-17 12:20:02 +08:00
leo
e102e49f45
code_style: remove unnecessary properties
Signed-off-by: leo <longshuang@msn.cn>
2025-06-17 11:29:24 +08:00
leo
dcdc52592c
code_review: PR #1423
Some checks are pending
Continuous Integration / Package (push) Blocked by required conditions
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Localization Check / localization-check (push) Waiting to run
Since we have already used `OnPropertyChanged`, move `ActualThemeVariantProperty` changed handler into it

Signed-off-by: leo <longshuang@msn.cn>
2025-06-16 12:31:37 +08:00
Nathan Baulch
e28b537f89
enhance: darker ChangeStatusIcon when using dark theme (#1423) 2025-06-16 12:19:14 +08:00
leo
ed66d2337b
ux: show stash message as tooltip when hovering it (#1419)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-16 12:09:02 +08:00
leo
0cbf1215e0
Merge branch 'master' into develop 2025-06-16 09:19:19 +08:00
leo
74d77ab704
Merge branch 'release/v2025.22' 2025-06-16 09:18:41 +08:00
leo
b617181fc5
version: Release 2025.22
Signed-off-by: leo <longshuang@msn.cn>
2025-06-16 09:18:31 +08:00
github-actions[bot]
9dedcacb2f doc: Update translation status and sort locale files 2025-06-16 01:15:05 +00:00
Javier J. Martínez M.
b22733b565
localization: update spanish translations (#1422)
add missing translations
2025-06-16 09:14:52 +08:00
Göran W
28844c59cf
perf: optimize the WorkingCopy.IsChanged() method (#1418)
Some checks failed
Continuous Integration / Build (push) Has been cancelled
Continuous Integration / Prepare version string (push) Has been cancelled
Continuous Integration / Package (push) Has been cancelled
* There's no need to populate a Dictionary just to diff the the "old" and "cur" Lists of Changes.
* If the two lists are of equal length and no change has occurred, we can assume that they are also reported in equal sort-order (by git-status).
* Thus, we only need to compare the two items at each successive index.
2025-06-13 19:23:20 +08:00
leo
f88652ffdd
code_review: PR #1417
Remove unnecessary namespace using

Signed-off-by: leo <longshuang@msn.cn>
2025-06-13 17:12:14 +08:00
Göran W
8dffdef48d
refactor: reduce redundant code in NumericSort (#1417)
* Refactor the 2 do-loops into simpler while-loops and replace the use of "tmp1/2" char-arrays with calls to String.Substring().
* Rename the "loc1/2" variables to "subLen1/2", for clarity, since they represent the lengths of the two extracted Substrings.

These changes make the logic more compact and easier to follow.
2025-06-13 17:06:35 +08:00
leo
158d926189
ux: new style for submodule diff
Signed-off-by: leo <longshuang@msn.cn>
2025-06-13 16:19:04 +08:00
leo
99b7208a54
enhance: prevent to start bisect if it is already running
Signed-off-by: leo <longshuang@msn.cn>
2025-06-13 15:32:31 +08:00
leo
05757ebf40
feature: supports to view .tiff images
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-12 18:15:25 +08:00
leo
cb6d6a233f
feature: show change tooltip in INFORMATION page
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-12 11:42:58 +08:00
leo
79650d1851
feature: supports to view .gif file as static image (not animated)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-12 11:32:24 +08:00
leo
7e2f3bec8c
enhance: clear commit message before merging/cherry-picking/rebasing/reverting to allow SourceGit read it from git (#1414)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-12 10:39:52 +08:00
leo
7de5991ecb
code_review: PR #1415
- Column for hotkey in `Reset` popup should use `Auto` for width
- Add `SelectionBoxItemTemplate` for current selected mode in `Reset` popup

Signed-off-by: leo <longshuang@msn.cn>
2025-06-12 09:54:53 +08:00
Nathan Baulch
ffac71b15f
code_style: general cleanup (#1415)
* code_style:  general cleanup

* code_style: whitespace cleanup
2025-06-12 09:35:37 +08:00
leo
35eda489be
feature: show local branch's track status in CheckoutAndFastForward popup
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-11 21:14:06 +08:00
leo
f59851f454
refactor: case-insensitive sorting
- `ToUpper` is not necessary to compare digit char with non-digit char
- use numeric sorting for commit decorators with same type

Signed-off-by: leo <longshuang@msn.cn>
2025-06-11 20:25:41 +08:00
leo
a128b67bd4
code_review: PR #1412
- Use `ViewModels.StashesPage.SelectedStash` instead of `sender is not ListBox { SelectedValue: Models.Stash stash }`
- In tags view, `SelectedItem` can be `Models.Tag` or `ViewModels.TagTreeNode`
- In logs window, `vm.SelectedLog` may be null

Signed-off-by: leo <longshuang@msn.cn>
2025-06-11 16:13:47 +08:00
Nathan Baulch
196b454ae8
feature: support delete key everywhere (#1412) 2025-06-11 15:35:43 +08:00
leo
5494093261
refactor: now all filesystem related trees/lists are sorted in case-insensitive mode
Signed-off-by: leo <longshuang@msn.cn>
2025-06-11 15:20:50 +08:00
leo
c3c7d32167
refactor: do not change original image aspect ratio and do not up-scale image in BLEND image-diff mode
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-11 10:25:24 +08:00
leo
7c1a894525
refactor: do not change original image aspect ratio and do not upscale image in SWIPE mode
Signed-off-by: leo <longshuang@msn.cn>
2025-06-11 10:12:03 +08:00
leo
bcefb773c1
code_style: remove comment
Some checks failed
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
Signed-off-by: leo <longshuang@msn.cn>
2025-06-10 17:19:37 +08:00
leo
aa1c8b1cc1
code_style: use combined expr
Signed-off-by: leo <longshuang@msn.cn>
2025-06-10 17:09:57 +08:00
github-actions[bot]
5e303d43d4 doc: Update translation status and sort locale files 2025-06-10 09:04:35 +00:00
leo
7d0536d94b
feature: when trying to checkout a local branch from its tracking upstream and it is behind the upstream, show Checkout & Fast-Forward popup
Signed-off-by: leo <longshuang@msn.cn>
2025-06-10 17:04:06 +08:00
Nathan Baulch
6c04f5390a
feature: double tap commit with tracked remote branch checks out local tracking branch (#1409) 2025-06-10 15:58:57 +08:00
leo
ee4d8a6a0e
code_review: PR #1408
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-10 11:18:20 +08:00
johanw1232
0ea4021a64
feature: update blame data when clicking/navigating commits (#1408)
* initial work on allowing navigation by clicking on commits in the blame window
* cleanup
2025-06-10 09:30:12 +08:00
leo
11a46dbc93
update: built-in github and unreal icons
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-09 17:35:20 +08:00
leo
69792b3262
refactor: do not upscale images
Signed-off-by: leo <longshuang@msn.cn>
2025-06-09 17:22:31 +08:00
leo
1b1dc2f666
code_style: remove unnecessary code
Signed-off-by: leo <longshuang@msn.cn>
2025-06-09 15:42:02 +08:00
leo
4302d7adb5
Merge branch 'master' into develop
Some checks failed
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
2025-06-09 09:31:46 +08:00
leo
932baeec53
Merge branch 'release/v2025.21' 2025-06-09 09:30:45 +08:00
leo
637e133f47
version: Release 2025.21
Signed-off-by: leo <longshuang@msn.cn>
2025-06-09 09:30:36 +08:00
github-actions[bot]
a1e76e9bea doc: Update translation status and sort locale files 2025-06-09 01:27:28 +00:00
AquariusStar
a8541a780e
localization: update translate Russian (#1404) 2025-06-09 09:27:10 +08:00
Sina Hinderks
d55f19586f
fix: issue tracker rule over keyword in subject (#1403)
Some teams use issue tracker numbers in front of the commit message
subject, followed by a colon.  It was not possible to use an issue
tracker rule in such cases, since the issue tracker number would be
interpreted as a keyword due to the colon and therefore displayed in
bold face instead of as a link into the issue tracker.
2025-06-09 09:26:27 +08:00
leo
a22c39519f
code_style: remove unnecessary code
Some checks failed
Continuous Integration / Build (push) Has been cancelled
Continuous Integration / Prepare version string (push) Has been cancelled
Continuous Integration / Package (push) Has been cancelled
Signed-off-by: leo <longshuang@msn.cn>
2025-06-08 11:54:54 +08:00
leo
84fb39f97a
code_review: PR #1402
- it's unnecessary to implement `IEnumerable` interface
- we should check `IsIntersecting` before creating `InlineElement` to avoid unnecessary works suck as running `git cat-file -t <hash>`
- sort whold list after all elements have been added to avoid unnecessary memmove in `Insert`

Signed-off-by: leo <longshuang@msn.cn>
2025-06-08 11:09:20 +08:00
Sina Hinderks
fe54d30b70
refactor: collecting inlines for subjects (#1402)
Instead of checking intersections of inline elements yourself before adding an inline element, the new class `InlineElementCollector` prevents intersections internally.

Additionally the inline elements are sorted by the new class, so it's no longer necessary to do this after adding the inline elements.
2025-06-08 08:47:03 +08:00
leo
ba4c0f0cd2
enhance: scroll to home when active revision file changed
Some checks are pending
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Continuous Integration / Build (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-06-07 20:53:34 +08:00
leo
2478d2953b
code_style: remove unnecessary code in DiffContext
Signed-off-by: leo <longshuang@msn.cn>
2025-06-07 20:42:45 +08:00
leo
74f52fb266
enhance: only show syntax-highlighting toggle if current revision content is a text file
Signed-off-by: leo <longshuang@msn.cn>
2025-06-07 20:27:52 +08:00
leo
f830b68f6a
ux: change foreground for some labels
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-07 12:20:09 +08:00
leo
d323a2064e
feature: supports RGBA16 pixel format
Signed-off-by: leo <longshuang@msn.cn>
2025-06-07 12:00:16 +08:00
leo
203c50350e
fix: wrong pfim image format
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-06 20:50:37 +08:00
leo
47012e29dc
fix: file extensions are case-insensitive
Some checks failed
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
Signed-off-by: leo <longshuang@msn.cn>
2025-06-06 18:47:36 +08:00
leo
8db033be99
code_review: PR #1392
- fix the issue that not all channel takes 8 bits
- if `PixelFormatTranscoder.Transcode` supports the same pixel formats, let it converts pixels automatically

Signed-off-by: leo <longshuang@msn.cn>
2025-06-06 18:23:10 +08:00
Henrik Andersson
a2ca071f08
feature: .dds image support (#1392)
* Added Pfim as 3rdparty lib

* Added support for parsing showing dds and tga images using Pfim

---------

Co-authored-by: Snimax <snimax@live.se>
2025-06-06 16:44:40 +08:00
Nathan Baulch
7bba40d03f
typos: (#1397) 2025-06-06 12:10:55 +08:00
leo
0c22409b7b
ux: new sort by time icon (#1393)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-06 11:37:56 +08:00
leo
f63fe8637b
feature: use different icon for sort mode (#1393)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-06-06 11:22:30 +08:00
github-actions[bot]
08665e45c1 doc: Update translation status and sort locale files 2025-06-06 02:45:35 +00:00
leo
3bb20868fc
refactor: remove unnecessary sort by name (descending) for tags (#1393)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-06 10:45:18 +08:00
leo
ac55bed812
enhance: revision file viewer
- show current file path
- add a toggle button to use global syntax highlighting setting (sometimes TextMateSharp will crash this app)

Signed-off-by: leo <longshuang@msn.cn>
2025-06-06 10:07:58 +08:00
leo
f003f67129
fix: should use file.SHA instead of _commit.SHA to query submodule's commit
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-05 21:54:09 +08:00
Göran W
f04b0c5d97
fix: prevent exception on repo with no commits/branches
Signed-off-by: leo <longshuang@msn.cn>
2025-06-05 21:28:32 +08:00
Göran W
406ace9e79
enhance: activate TabsDropdownItem on Tapped instead of DoubleTapped
Signed-off-by: leo <longshuang@msn.cn>
2025-06-05 21:28:10 +08:00
leo
464fe74580
code_review: commit b969ac161a
- The return code of `AutoRemoveInvalidNode`  is never used
- It's not necessary to sort all nodes after re-scan default clone dir. Because `FindOrAddNodeByRepositoryPath` makes sure added node is ordered
- Add a new parameter `save` to `FindOrAddNodeByRepositoryPath` method, and disable it while scanning. Instead, we will save it after scan finished.

Signed-off-by: leo <longshuang@msn.cn>
2025-06-05 21:27:19 +08:00
Göran W
b969ac161a
enhance: unify sorting of RepositoryNode tree, unconditional sort & save after rescan 2025-06-05 21:19:25 +08:00
Göran W
88c38b4139
enhance: unified all file-path normalization - use char-replace, trim trailing slash 2025-06-05 21:17:18 +08:00
Göran W
54c05ac35a
fix: remove trailing slash in paths, to avoid failing comparisons.
This is needed since DirectoryInfo.Fullname (and .FullPath) will not "normalize" trailing slashes, so direct equality tests are error-prone.
This fixes a bug in ScanRepositories.GetUnmanagedRepositories(), where not all Git repo folders were added.
(Also, corrected a variable name from 'founded' to 'found'.)
2025-06-05 21:15:28 +08:00
Göran W
75c32c1a01
typo: corrected spelling error in App.GetLauncher() method 2025-06-05 21:15:28 +08:00
leo
a023a9259b
refactor: rewrite lfs pointer detection and image loading
Signed-off-by: leo <longshuang@msn.cn>
2025-06-05 21:06:31 +08:00
leo
eebadd67a1
feature: remember the last active tab index in lfs-image diff view
Some checks are pending
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Continuous Integration / Build (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-06-05 09:18:19 +08:00
leo
f716c5ee1e
refactor: use existing QueryFileContent command
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-04 21:30:08 +08:00
leo
ed496a41fb
feature: supports to view image diff when lfs object points to a image
Signed-off-by: leo <longshuang@msn.cn>
2025-06-04 20:53:42 +08:00
leo
06a77502bc
fix: crash when clicking Previous Difference without visual lines (#1385)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Always scroll to top when the state of `Show All Lines` changed

Signed-off-by: leo <longshuang@msn.cn>
2025-06-04 13:13:28 +08:00
leo
c2187edbe9
fix: running git command in UIThread via context menu entry blocks whole app (#1384)
Some checks failed
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 23:36:15 +08:00
github-actions[bot]
d85f82e171 doc: Update translation status and sort locale files 2025-06-03 13:38:04 +00:00
leo
f7c10d0b33
feature: supports to load avatar from local image and save it to disk
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 21:37:47 +08:00
leo
25e272fa55
ux: layout of filter mode toggle buttons
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 20:28:44 +08:00
leo
98041c803e
feature: supports re-order custom actions (#1346)
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 20:24:30 +08:00
leo
ee2e7d0127
enhance: ignores $ char when measuring contents in NamedHighlightedTextBlock
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 14:12:23 +08:00
leo
6517e78ab6
enhance: enable StaysOpenOnClick for filter mode in graph context menu item
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 14:06:53 +08:00
leo
bf43dd828a
ux: new style for ref's Visibility in Graph context menu item
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 12:34:49 +08:00
leo
cd009bda6b
ux: enable Use monospace font only in text editor by default
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 10:15:58 +08:00
leo
cd8ff2e9bf
Merge branch 'master' into develop
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
2025-06-03 09:27:51 +08:00
leo
0594196dee
Merge branch 'release/v2025.20' 2025-06-03 09:27:03 +08:00
leo
425395da29
version: Release 2025.20
Signed-off-by: leo <longshuang@msn.cn>
2025-06-03 09:26:57 +08:00
leo
6e501b1ee4
feature!: now SourceGit requires git >= 2.25.1
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-06-02 22:38:00 +08:00
leo
7b05b011aa
fix: USE THEIRS for AU conflict and USE MINE for UA conflict
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-02 13:03:38 +08:00
leo
f1052c3efc
refactor: use git reset --hard HEAD to discard all changes and use git restore --staged to unstage changes in text diff view
Signed-off-by: leo <longshuang@msn.cn>
2025-06-02 12:50:58 +08:00
leo
78f9ae2fa9
refactor: rewrite git restore integration
Signed-off-by: leo <longshuang@msn.cn>
2025-06-02 12:14:22 +08:00
leo
80df53cf04
ux: move hunk-based operation button away from scrollbar (#1382)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-01 20:04:15 +08:00
leo
57004c4baf
code_style: run dotnet format
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-06-01 11:23:04 +08:00
leo
fa004ce31b
enhance: unstaged renamed file should use git restore --staged <path> <org_path> instead of git restore --staged <path>
Signed-off-by: leo <longshuang@msn.cn>
2025-06-01 11:19:41 +08:00
leo
6620bd193e
ux: remove tooltip for USE THEIRS and USE MINE button
Signed-off-by: leo <longshuang@msn.cn>
2025-06-01 11:09:31 +08:00
leo
26307e2343
refactor: new tooltip for change
Signed-off-by: leo <longshuang@msn.cn>
2025-06-01 10:34:24 +08:00
leo
db5bb0aec9
fix: must convert the relative path to absolute before send it to first instance (#1376)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-31 21:26:01 +08:00
leo
dd432c63e8
enhance: when counting commits in Statistics, if the authors have the same e-mail address, the commits are considered to be from the same person (#1380)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-31 18:52:15 +08:00
github-actions[bot]
b94f26a937 doc: Update translation status and sort locale files
Some checks failed
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
2025-05-31 04:15:23 +00:00
Javier J. Martínez M.
8e5d5b946e
localization: update spanish translations (#1379)
add missing translations
2025-05-31 12:15:06 +08:00
leo
a9734ea8e9
code_style: remove unused code
Signed-off-by: leo <longshuang@msn.cn>
2025-05-31 11:33:22 +08:00
github-actions[bot]
e22f0f8513 doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
2025-05-30 10:20:10 +00:00
leo
8b17f3b1f4
localization: change Compare with HEAD to Compare with <current_branch_name>
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 18:19:46 +08:00
leo
7934496cff
feature: reset non-active branch to selected commit should not depends on upstream
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 17:56:06 +08:00
leo
188408fdfc
project: remove duplicated attributes in csproj
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 17:21:49 +08:00
leo
bc5deac9fe
fix: OpenFolderPickerAsync raise exception when selected a drive root such as E:\
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 17:12:56 +08:00
leo
1bd2044589
ux: show conflict description in change status icon
Some checks are pending
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Continuous Integration / Build (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 11:20:43 +08:00
leo
f0c77ffeb8
code_style: run dotnet format
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 11:16:07 +08:00
leo
60cd210b80
fix: using theirs or mine does not work if it is deleted by ours or theirs
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 11:13:29 +08:00
leo
75015d550c
ux: show conflict short format in changes view
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 10:42:02 +08:00
leo
e40ca4bbe0
refactor: use git restore instead of git reset to unstage local changes (#1373)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-30 09:43:45 +08:00
leo
46231a759c
code_style: run dotnet format
Some checks failed
Continuous Integration / Build (push) Has been cancelled
Continuous Integration / Prepare version string (push) Has been cancelled
Localization Check / localization-check (push) Has been cancelled
Continuous Integration / Package (push) Has been cancelled
Signed-off-by: leo <longshuang@msn.cn>
2025-05-28 15:17:05 +08:00
leo
fbc8edcc13
feature: show conflict reason
Signed-off-by: leo <longshuang@msn.cn>
2025-05-28 14:20:56 +08:00
github-actions[bot]
3437f5f4a9 doc: Update translation status and sort locale files 2025-05-28 02:19:23 +00:00
leo
40bf69bff3
ux: move some buttons to page switcher (#1370)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-28 10:18:34 +08:00
leo
2aac6779a5
fix: squash should not change the original author
Signed-off-by: leo <longshuang@msn.cn>
2025-05-28 09:50:14 +08:00
cdammanintopix
9affca1fb2
fix: arguments order for checkout command (#1368)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
2025-05-27 17:43:33 +08:00
Gadfly
729e06d5c0
fix: SaveAsPatch for untracked changes in stash (#1366)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
2025-05-26 22:50:29 +08:00
leo
826619e7c9
fix: new added file in stash changes may not come from untracked file but from staged files (#1358)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-26 22:13:10 +08:00
github-actions[bot]
056b90a5ae doc: Update translation status and sort locale files
Some checks failed
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Continuous Integration / Build (push) Waiting to run
Localization Check / localization-check (push) Has been cancelled
2025-05-26 04:28:23 +00:00
leo
0641a22230
feature: allow to reset author when --amend is enabled for committing 2025-05-26 12:28:00 +08:00
leo
d3bc85418e
Merge branch 'master' into develop 2025-05-26 09:56:00 +08:00
leo
4887252870
Merge branch 'release/v2025.19' 2025-05-26 09:45:48 +08:00
leo
860f6f2369
version: Release 2025.19
Signed-off-by: leo <longshuang@msn.cn>
2025-05-26 09:45:43 +08:00
leo
cfc80d41a1
fix: sometimes track status is not correct because the local branch name is ambiguous to git
Signed-off-by: leo <longshuang@msn.cn>
2025-05-26 09:42:46 +08:00
leo
ac26d5bb06
fix: can not squash and fixup until first picked/edit/reword commit in interactive rebase exists list (#1362)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-05-25 13:46:27 +08:00
github-actions[bot]
d7c3bb7150 doc: Update translation status and sort locale files 2025-05-25 05:05:31 +00:00
AquariusStar
39d955b033
localization: update russian translate (#1363) 2025-05-25 13:05:14 +08:00
leo
0e35c56529
enhance: disable squash and fixup for the first commit in interactive rebase list (#1362)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-25 13:03:36 +08:00
leo
22339ab619
enhance: allow to use arrow keys to select changes up/down after stage/unstage previous selected changes by hotkey (#1361)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-24 21:29:48 +08:00
leo
ef53dd0025
refactor: use a new Models.ChangeState.Conflicted to represent all the unmerged file state (#1359)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-24 20:25:05 +08:00
Göran W
30d4c1008a
enhance: don't show unmerged files in STAGED area (#1359)
Unmerged files (i.e unresolved conflicts) should only appear in the Unstaged area, and not "duplicated" in the Staged area.

Motivation:
* The user-friendly git-status command does not show these as "Changes to be committed".
* If they appear in the Staged area, they are quite redundant since the Diff view will just show "No changes or only EOL changes".
* Some other Git UIs (like Fork) don't show these as Staged items either.

NOTE: According to docs for the git-status "Short Format" (and --porcelain=v1), the XY fields for Unmerged paths do NOT actually represent the Index & Working-tree states, instead they represent the states introduced by each HEAD in the merge (i.e Ours & Theirs, relative to Base).
2025-05-24 19:42:10 +08:00
leo
ca33107a45
code_review: PR #1360
Signed-off-by: leo <longshuang@msn.cn>
2025-05-24 19:26:17 +08:00
Göran W
4363b8b6aa
refactor: add new constant Models.Commit.EmptyTreeSHA1 (#1360) 2025-05-24 19:23:28 +08:00
Göran W
f3fe90b2e1
fix: IsConflictResolved check should not be done for submodules (#1356)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
A submodule conflict is not resolved until it's Staged.
2025-05-24 09:40:17 +08:00
leo
e28b75b860
enhance: include stdout in error popup when git process (in Exec mode, we do not need to parse its output) exit with non-zero code
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 16:12:40 +08:00
leo
3377886bab
enhance: filter hint: blocks
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 14:13:48 +08:00
leo
4807cd5eb2
fix: if font family name contains '#', make sure we have that built-in font
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 13:32:02 +08:00
github-actions[bot]
d21b790784 doc: Update translation status and sort locale files
Some checks failed
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
2025-05-23 04:25:41 +00:00
Javier J. Martínez M.
764ae31239
localization: update spanish translations (#1355)
* localization: update spanish translations

add missing translations

* Update es_ES.axaml

Quickfix for `Text.DeinitSubmodule.Force` translation
2025-05-23 12:25:29 +08:00
github-actions[bot]
38d67d7f17 doc: Update translation status and sort locale files 2025-05-23 03:28:10 +00:00
leo
76a197aae7
feature: supports to overwrite existing branch while creating new branch (#1349)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 11:27:45 +08:00
leo
594ffc0d1a
localization: fix typo in en_US for Text.DeinitSubmodule.Force
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 10:38:44 +08:00
leo
fb1f5638ce
code_style: simpfy context menu creation for blame editor
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 10:31:11 +08:00
leo
492da8dd57
fix: blame highlight background did not update when using scrollbar (#1354)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 10:27:48 +08:00
leo
0ae39faad1
enhance: do not show hint: messages in error popup, but leave it in git command logs (#1348)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 10:05:09 +08:00
leo
c112549b69
refactor: query branch head after operation finished to avoid branch head mismatch
Signed-off-by: leo <longshuang@msn.cn>
2025-05-23 09:40:15 +08:00
Göran W
9fb8af51ff
code_style: move hardcoded brush/strings (for outlier Conflict-icon) into named constants (#1350)
This makes code more consistent and gives better overview of all the icons.
(Potentially, this special/outlier icon could be moved into the existing arrays as an extra ChangeState enum-value.)
2025-05-23 09:18:05 +08:00
leo
1ee7d1184e
enhance: only refresh submodules when it is needed (#1344)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-22 11:02:54 +08:00
Göran W
44c83ef342
enhance: added more FileSystemWatcher patterns, to improve handling (#1345)
Skip files frequently updated by Git fsmonitor--daemon and Visual Studio, to ease debugging and for early exit.
Check for HEAD and ORIG_HEAD under .git/modules/<submodule>/, to improve handling of submodules.
Check for MERGE_HEAD and AUTO_MERGE under .git/, to improve handling of submodules.
2025-05-22 09:11:28 +08:00
leo
c3ac59ee1a
enhance: refresh submodules after .gitmodules file changed
Some checks failed
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Has been cancelled
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 20:51:29 +08:00
leo
c73f775aa5
enhance: mark submodule having local changes even if there are only untracked files (#1344)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 20:47:10 +08:00
M-L-Ml
afbd0d7135
fix: typo in name SetupExternalTools (#1343) 2025-05-21 20:35:22 +08:00
github-actions[bot]
bf39673b21 doc: Update translation status and sort locale files 2025-05-21 12:34:51 +00:00
leo
b0c0c46441
feature: supports to de-initialize a submodule (#1272)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 20:34:33 +08:00
leo
1fef7a7baa
perf: only update uninited or outdated submodules
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 17:51:00 +08:00
github-actions[bot]
1872740265 doc: Update translation status and sort locale files 2025-05-21 09:18:49 +00:00
leo
09d0122e26
refactor: rewrite git pull command
If we do not provide pulling remote branch, it will auto fetch all branches first.

Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 17:18:25 +08:00
github-actions[bot]
7728f4ffbf doc: Update translation status and sort locale files 2025-05-21 08:54:46 +00:00
leo
936160ea5c
feature: supports --recurse-submodules on pull (#1342)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 16:54:23 +08:00
leo
d73ae83b01
feature: supports to use relative path in remote URL (#1339)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 16:29:33 +08:00
leo
5e05c008fc
refactor: simplfy the regex to check remote's URL with HTTP/HTTPS/GIT protocol
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 15:09:36 +08:00
leo
598ba6d9f6
refactor: rewrite Remote.IsValidURL (#1339)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 14:48:21 +08:00
leo
3232e6f313
fix: on Windows, the correct file protocol url format is file:///<driver>:/path/to/file_or_dir (#1339)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 14:18:06 +08:00
leo
0a6b1faa65
feature: support git:// protocol (#1339)
Some checks are pending
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Continuous Integration / Build (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 09:36:41 +08:00
leo
71b90a82b6
refactor: remove validation for relative path while adding submodule (#1339)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 09:27:02 +08:00
leo
d304c50e7f
enhance: show custom action output in popup
Some checks are pending
Localization Check / localization-check (push) Waiting to run
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 00:16:19 +08:00
leo
438aa76695
feature: support to use relative path as submodule's url when adding new submodule (#1339)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-21 00:10:10 +08:00
leo
ece51fbd32
fix: remove binding error in debug mode (#1338)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 23:02:45 +08:00
leo
224f7a949a
fix: since can not been used in HotKey property and ⌘+⌥+D has been used to show Dock, change the hotkey to open external merge tool to Ctrl+Shift+D/⌘+⇧+D
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 23:01:21 +08:00
leo
e6fdc778b7
fix: remove binding error in debug mode (#1338)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 21:48:58 +08:00
leo
53c6fc8999
fix: remove binding error in debug mode (#1338)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 21:44:28 +08:00
leo
f0d1d460a9
fix: remove binding error in debug mode (#1338)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 21:35:14 +08:00
leo
3386cb177b
refactor: rewrite git flow init
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 21:26:41 +08:00
leo
4d5be9f280
refactor: rewrite git-flow integration
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 21:08:00 +08:00
leo
6fa454ace8
fix!: same hotkey for opening external diff tool and discard block (#1337)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 17:32:06 +08:00
leo
75b7724d44
refactor: implement IDisposable instead of calling custom Cleanup
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 17:24:00 +08:00
leo
550493b572
enhance: prevent requesting worktree files more than once time
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 16:49:00 +08:00
leo
eb183589f5
enhance: prevent requesting revision files more than once time
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 16:32:57 +08:00
github-actions[bot]
d56c6a5030 doc: Update translation status and sort locale files 2025-05-20 04:24:40 +00:00
leo
f9b6116a76
feature: supports reset branch to selected commit without checkout (#1247) (#1318)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 12:24:07 +08:00
Gadfly
12d2b7721c
fix: Trim and normalize commit message history line endings (#1335)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
2025-05-20 10:56:02 +08:00
leo
119b0fe95c
feature: log output of custom action if Wait for action exit enabled (#1334)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-20 09:17:00 +08:00
github-actions[bot]
1dfb629cef doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
2025-05-19 04:22:07 +00:00
leo
0e2bb1b276
feature: show commit changes count (#1306)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-19 12:21:50 +08:00
leo
57ee1f7dbd
fix: wrong hotkey tip for open Preferences window
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-19 10:55:25 +08:00
leo
aaf53ac694
enhance: try to cancel switcher first then other popup
Signed-off-by: leo <longshuang@msn.cn>
2025-05-19 10:08:37 +08:00
leo
736991198f
Merge branch 'master' into develop 2025-05-19 09:45:11 +08:00
leo
7dd1389c25
Merge branch 'release/v2025.18' 2025-05-19 09:43:59 +08:00
leo
341ac26576
version: Release 2025.18
Signed-off-by: leo <longshuang@msn.cn>
2025-05-19 09:43:48 +08:00
leo
aff003fd6d
enhance: cleanup unused resources
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
Signed-off-by: leo <longshuang@msn.cn>
2025-05-18 22:00:35 +08:00
qiufengshe
b78f6b0ea8
perf: minimize temporary strings for better performance (#1332) 2025-05-18 20:52:05 +08:00
leo
5e85f6fefe
enhance: auto-select the first page by default
Signed-off-by: leo <longshuang@msn.cn>
2025-05-18 20:47:38 +08:00
github-actions[bot]
52991351af doc: Update translation status and sort locale files 2025-05-18 12:34:17 +00:00
leo
4b849d9d5c
ux: update workspace/page switcher popup layout
Signed-off-by: leo <longshuang@msn.cn>
2025-05-18 20:33:55 +08:00
github-actions[bot]
6b083dcd3e doc: Update translation status and sort locale files 2025-05-18 11:36:39 +00:00
leo
9614b995d8
refactor: workspace/page switcher (#1330)
- add `Switch Tab` popup
- change hotkey to open `Preferences` to `Ctrl+,/⌘+,`
- change hotkey to open `Switch Workspace` to `Ctrl+Shift+P/⌘+⇧+P`
- change hotkey to open `Switch Tab` to `Ctrl+P/⌘+P`

Signed-off-by: leo <longshuang@msn.cn>
2025-05-18 19:36:17 +08:00
github-actions[bot]
36c2e083cc doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
2025-05-18 07:02:46 +00:00
AquariusStar
fd35e0817d
localization: update russian translate (#1331) 2025-05-18 15:02:30 +08:00
github-actions[bot]
d429a6426a doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
2025-05-17 12:14:32 +00:00
leo
4c1ba717a7
refactor: rewrite workspace switcher (#1267)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-17 20:14:09 +08:00
github-actions[bot]
bd553405c2 doc: Update translation status and sort locale files 2025-05-17 10:37:25 +00:00
leo
f121975a28
code_review: PR #1328
* remove hotkey to open workspace dropdown menu
* call orignal `ViewModels.Launcher.SwitchWorkspace` directly in view
* add missing translation for Chinese

Signed-off-by: leo <longshuang@msn.cn>
2025-05-17 18:37:02 +08:00
github-actions[bot]
ea320d2cdf doc: Update translation status and sort locale files
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run
2025-05-17 05:17:22 +00:00
popara
01945f231e
Added workspaces shortcuts (#1328)
- added Alt+Space for opening Workspaces context menu (which can then be navigated normally with arrows)
- added Alt+1 through Alt+9 for switching to corresponding workspace
2025-05-17 13:17:10 +08:00
github-actions[bot]
506dbc218c doc: Update translation status and sort locale files 2025-05-17 05:12:20 +00:00
Javier J. Martínez M.
d3a740fb95
localization: update spanish translations (#1329)
add missing translations
2025-05-17 13:12:01 +08:00
leo
d3d0e7b15c
ux: thinner border for default avatar
Some checks are pending
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Signed-off-by: leo <longshuang@msn.cn>
2025-05-17 08:13:19 +08:00
Gadfly
879b84ac20
enhance: Show the stderr content from QueryLocalChanges (#1327) 2025-05-17 07:58:47 +08:00
github-actions[bot]
7f86ad9f22 doc: Update translation status and sort locale files 2025-05-16 12:08:16 +00:00
Leonardo
0c9cb41e68
localization: new keys translated to italian (#1323) 2025-05-16 20:08:04 +08:00
leo
86f27c5e58
refactor: generate hash based default avatar instead of simple label (#1322)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-16 17:55:02 +08:00
leo
1f0ab2bfec
refactor: simpfy SourceGit.Views.BranchTreeNodeIcon
Signed-off-by: leo <longshuang@msn.cn>
2025-05-16 13:43:41 +08:00
leo
fd935259aa
refactor: build tags view data in viewmodels instead of views
Signed-off-by: leo <longshuang@msn.cn>
2025-05-16 12:22:52 +08:00
github-actions[bot]
f46bbd01cd doc: Update translation status and sort locale files 2025-05-16 03:32:09 +00:00
leo
ed1351b1f7
feature: supports to show submodules as tree or list (#1307)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-16 11:31:53 +08:00
leo
d299469613
ux: show tooltip at right of hovered item in repository's left side bar (#1317)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-16 10:47:38 +08:00
leo
e4490d87dc
code_review: PR #1314
Signed-off-by: leo <longshuang@msn.cn>
2025-05-16 09:45:26 +08:00
Martin Smith
85b223a3d0
Task/UI usability tweaks (#1314)
* Allow About to center in parent
* About closes on Escape
* ConfirmEmptyCommit dialog closes on Escape
* Ignore Dev file
* Missed condition

---------

Co-authored-by: Martin Smith <martin.smith@purplebricks.com>
2025-05-16 09:27:42 +08:00
github-actions[bot]
6c62789c4c doc: Update translation status and sort locale files 2025-05-16 01:20:43 +00:00
AquariusStar
85e08f5eea
localization: update russian localization (#1319) 2025-05-16 09:20:31 +08:00
leo
463d161ac7
refactor: show submodule as tree instead of list (#1307) 2025-05-14 17:55:28 +08:00
github-actions[bot]
5ec51eefb9 doc: Update translation status and sort locale files 2025-05-14 08:02:08 +00:00
leo
bc5c4670de
feature: supports to use Ctrl+D/⌘+D to open in external diff/merge tool (#1312) 2025-05-14 16:01:47 +08:00
leo
d3363429df
ux: new style for submodule tooltip (#1307) 2025-05-14 15:49:42 +08:00
github-actions[bot]
f83b6c24ae doc: Update translation status and sort locale files 2025-05-14 06:27:00 +00:00
leo
61bb0f7dc7
feature: show submodule's URL in tooltip (#1307) 2025-05-14 14:26:33 +08:00
leo
20a239621b
fix: can not open submodule that has not been initialized 2025-05-14 11:48:44 +08:00
github-actions[bot]
9e91494a20 doc: Update translation status and sort locale files 2025-05-14 03:36:15 +00:00
leo
d71189c705
feature: tooltip for submodule list item (#1307) 2025-05-14 11:35:34 +08:00
leo
55232aeddd
project: ignore custom script files
Signed-off-by: leo <longshuang@msn.cn>
2025-05-13 22:55:24 +08:00
leo
6bf930a9e0
feature: show tags count in tags tree (#1306)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-13 22:50:10 +08:00
Göran W
5b72b15cf2
feature: show remote's URL in tooltip for relevant BranchTreeNodes (#1310) 2025-05-13 22:36:10 +08:00
leo
0e61a0196b
fix: right caption buttons should not visible on macOS (#1311)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-13 22:34:53 +08:00
leo
7bb4e355bd
feature: show branches count in branch tree (#1306) 2025-05-13 19:28:52 +08:00
leo
57d15dc6d3
fix: git submodule status may return lines that start as U character (#1307) 2025-05-13 18:11:51 +08:00
leo
65808e5f58
fix: filter with local branch should not include invalid upstream (gone) (#1308) 2025-05-13 17:59:51 +08:00
leo
cf7b61dd71
refactor: new way to display item count 2025-05-13 17:50:47 +08:00
leo
ac1bd7ca85
ux: hide tag message if it's the same with its name (#1305) 2025-05-13 14:22:41 +08:00
leo
142ee5a327
ux: use localized text instead of hard-coded string annotated (#1305) 2025-05-13 13:01:15 +08:00
leo
afc8a772dd
ux: new style for tag's tooltip (#1305) 2025-05-13 12:26:33 +08:00
leo
8a45e25106
refactor: rewrite custom WM_NCHITTEST implementation on Windows 2025-05-13 10:19:51 +08:00
leo
4e41a6207a
enhance: display tag's name instead of nothing while showing tooltip of tag without message 2025-05-13 10:01:41 +08:00
Göran W
a5c25cf9fe
enhance: add border around tag name, makes tooltip work as for branches
(cherry picked from commit 6a5e6d12d70f52e5777cc4edc4022fed870151d4)
2025-05-13 09:38:09 +08:00
leo
11a9d7fdd8
enhance: force using --no-sign to ignore tag.gpgsign configuration while creating lightweight tag
Signed-off-by: leo <longshuang@msn.cn>
2025-05-13 09:24:00 +08:00
leo
ef4b639f8e
code_style: move platform dependent code to initialize window to namespace SourceGit.Native
Signed-off-by: leo <longshuang@msn.cn>
2025-05-12 21:52:50 +08:00
leo
c62b4a031f
perf: return HTCLIENT directly when window is fullscreen or maximized
Signed-off-by: leo <longshuang@msn.cn>
2025-05-12 18:09:25 +08:00
leo
af9cf6ba6a
ux: force using 4 * RenderScaling as resize border size on Windows
Signed-off-by: leo <longshuang@msn.cn>
2025-05-12 17:57:49 +08:00
leo
641098ffb2
ux: better content padding for maximized window on Windows
Signed-off-by: leo <longshuang@msn.cn>
2025-05-12 16:27:54 +08:00
leo
fcad8eeadc
Merge branch 'master' into develop 2025-05-12 09:24:48 +08:00
leo
01625ada1a
Merge branch 'release/v2025.17' 2025-05-12 09:23:46 +08:00
leo
88dc12275a
version: Release 2025.17
Signed-off-by: leo <longshuang@msn.cn>
2025-05-12 09:22:28 +08:00
Bailey Allen
bac21c5714
enhance: added support for kitty terminal on macOS and Linux. (#1300) 2025-05-12 09:17:20 +08:00
github-actions[bot]
19a51f227b doc: Update translation status and sort locale files 2025-05-12 01:13:27 +00:00
Javier J. Martínez M.
b6d618a6d7
localization: update spanish translations (#1302)
* localization: update spanish translations

add missing translations

* localization: update spanish translations

add missing translations
2025-05-12 09:13:13 +08:00
github-actions[bot]
2573553e01 doc: Update translation status and sort locale files 2025-05-12 01:12:59 +00:00
AquariusStar
9dd0beb61f
localization: update russian translate (#1301) 2025-05-12 09:12:49 +08:00
leo
029fd6933f
refactor: new way to discard selected or all local changes
This modification aims to solve the problem that the deleted submodule cannot be discarded.

Signed-off-by: leo <longshuang@msn.cn>
2025-05-09 22:57:46 +08:00
leo
0f6c8976af
refactor: rewrite checkout/create branch with submodules
Signed-off-by: leo <longshuang@msn.cn>
2025-05-09 18:12:30 +08:00
leo
e446e97f28
fix: remove testing code for git checkout command
Signed-off-by: leo <longshuang@msn.cn>
2025-05-09 17:22:17 +08:00
leo
3e530de9cc
enhance: update submodules individually (#1272)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-09 17:12:12 +08:00
leo
6cf1b20ea6
refactor: context menu for commit change and revision file
Signed-off-by: leo <longshuang@msn.cn>
2025-05-09 14:11:15 +08:00
github-actions[bot]
321ccf9622 doc: Update translation status and sort locale files 2025-05-09 02:47:52 +00:00
leo
ebe0e61367
feature: support to enable --squash and --push option while finishing git-flow branches (#1290)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-09 10:47:36 +08:00
leo
e8bf58f6c3
code_review: PR #1292
Use syntax `String.AsSpan(int start, int len)` instead of `String.AsSpan().Slice(int start, int len)`

Signed-off-by: leo <longshuang@msn.cn>
2025-05-09 09:30:00 +08:00
qiufengshe
15ee2dac91
perf: minimize temporary strings for better performance (#1292) 2025-05-09 09:19:33 +08:00
github-actions[bot]
5d1601086f doc: Update translation status and sort locale files 2025-05-09 01:16:10 +00:00
AquariusStar
3eaa24a993
localization: update and fix translation russian (#1291) 2025-05-09 09:15:59 +08:00
leo
6986e1ac24
code_style: calculate bounds only when it is needed
Signed-off-by: leo <longshuang@msn.cn>
2025-05-08 13:42:21 +08:00
leo
2c8370fa92
refactor: get graph clip width from grid column definition directly
Signed-off-by: leo <longshuang@msn.cn>
2025-05-08 13:39:27 +08:00
leo
008708f07c
ux: use larger font size for commit ref label
Signed-off-by: leo <longshuang@msn.cn>
2025-05-08 13:13:22 +08:00
leo
832fcd7487
fix: offset of commit graph does not look quite right (#1287)
This is because that when using `VirtualizingStackPanel`, the `Bounds.Height` of `ListBoxItem` may not be the same with its `Height` setted in axaml.

Signed-off-by: leo <longshuang@msn.cn>
2025-05-08 12:22:23 +08:00
leo
6df38ad970
ux: new style for inline code in commit subject
Signed-off-by: leo <longshuang@msn.cn>
2025-05-07 20:23:06 +08:00
github-actions[bot]
0a7b973388 doc: Update translation status and sort locale files 2025-05-07 11:08:51 +00:00
Christopher Göttfert
6b050fa557
localization: updated german translations (#1284) 2025-05-07 19:08:39 +08:00
leo
417ab3ecc2
ux: layout for revision compare targets
Signed-off-by: leo <longshuang@msn.cn>
2025-05-07 09:52:26 +08:00
leo
a413df6f89
code_style: run dotnet format
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 20:56:45 +08:00
leo
ddf643c081
ux: new style for revision/branch compare targets
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 20:52:43 +08:00
leo
bbc840a5cb
perf: set/update TimeToSort while creating branch nodes
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 19:26:06 +08:00
github-actions[bot]
c8e21673e4 doc: Update translation status and sort locale files 2025-05-06 10:24:59 +00:00
leo
e45e37d305
feature: supports sort branches by committer date (#1192)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 18:24:34 +08:00
github-actions[bot]
b7fa04d141 doc: Update translation status and sort locale files 2025-05-06 07:52:23 +00:00
leo
93a5d7baea
feature: supports to visit remote repository in web browser (#1265)
- combine `Open in File Manager`, `Open in Terminal` and `Open with external editor` into one dropdown menu
- add `Visit $REMOTE in Browser`

Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 15:51:57 +08:00
leo
e4e2f7b3a7
ux: use smaller font size for inline code in commit subject
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 14:49:54 +08:00
leo
eae6d10784
enhance: only log exception in popup task (#1281)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 12:17:48 +08:00
github-actions[bot]
4bc5b90e6b
doc: Update translation status and sort locale files
(cherry picked from commit 15445d02379020144239886bc87380ae38c2018a)
2025-05-06 12:02:19 +08:00
leo
df29edd8f0
feature: make --recurse-submdoules an option while trying to checkout branch with submodules (#1272)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 12:01:58 +08:00
leo
054bbf7e0c
enhance: do not override core.autocrlf configure while reading file diff (#1278)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 09:39:03 +08:00
leo
ce00fa6c88
Merge branch 'master' into develop 2025-05-06 09:23:20 +08:00
leo
a960e14368
Merge branch 'release/v2025.16' 2025-05-06 09:22:58 +08:00
leo
867edd9453
version: Release 2025.16
Signed-off-by: leo <longshuang@msn.cn>
2025-05-06 09:22:49 +08:00
github-actions[bot]
aee4ce6387 doc: Update translation status and sort locale files 2025-05-06 01:17:55 +00:00
Javier J. Martínez M.
d92d279fbe
localization: update spanish translations (#1279)
add missing translations
2025-05-06 09:17:45 +08:00
github-actions[bot]
5e080279ce doc: Update translation status and sort locale files 2025-05-04 09:36:42 +00:00
AquariusStar
704c6f589d
localization: update and fix translation russian (#1276) 2025-05-04 17:36:30 +08:00
broknecho
666275c747
feature: add Meld as an option for external merge tool on Windows (#1275) 2025-05-04 11:24:11 +08:00
leo
c0c52695a3
code_style: remove unused code
Signed-off-by: leo <longshuang@msn.cn>
2025-05-03 21:31:10 +08:00
Alen Šiljak
c529fab869
feature: close repository configuration dialog when user pressed Esc (#1269) 2025-05-03 21:18:24 +08:00
leo
4b2983b330
fix: commit detail panel is overlapping history when resizing (#1273)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-03 21:09:43 +08:00
leo
8c1d397480
fix: inline blocks is not sorted in order (#1274)
Signed-off-by: leo <longshuang@msn.cn>
2025-05-03 20:52:40 +08:00
leo
007acb3fa6
project: remove unused scripts
Signed-off-by: leo <longshuang@msn.cn>
2025-04-30 21:40:01 +08:00
leo
3b0c57be84
feature: supports to re-order workspaces (#1261)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-30 21:39:03 +08:00
github-actions[bot]
61bc42612e doc: Update translation status and sort locale files 2025-04-30 13:06:53 +00:00
leo
fe677d40c1
feature: supports search commits by change content (#1263)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-30 21:05:53 +08:00
leo
9bde797b24
fix: make sure the new pattern is appended as a new line (#1264)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-30 20:41:00 +08:00
leo
7501588c95
enhance: quit application after main window has been closed
Signed-off-by: leo <longshuang@msn.cn>
2025-04-30 15:03:14 +08:00
leo
80aead3a17
feature: add dirty state indicator icon to repository tab (#1227)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-30 11:01:39 +08:00
leo
847a1e727b
refactor: enable --ignore-cr-at-eol in diff by default
Signed-off-by: leo <longshuang@msn.cn>
2025-04-30 09:26:34 +08:00
leo
98dd37a9bc
localization: update tranlation for Text.Diff.IgnoreWhitespace
This is because that in `git diff` command the `--ignore-all-space` option will also ignore line-ending changes (`--ignore-cr-at-eol`)

Signed-off-by: leo <longshuang@msn.cn>
2025-04-29 22:49:20 +08:00
leo
95ea0a6ba6
ux: Ignore Whitespace and EOL Changes should always be visible (#1260)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-29 22:21:38 +08:00
leo
b9dc5a8164
feature: parse url in commit message (#1133)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-29 18:08:35 +08:00
leo
63803c9b88
feature: show command running time in logs window (#1253)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-29 16:36:05 +08:00
leo
825b74c2a3
refactor: use String.AsSpan(int, int) instead of String.AsSpan().Slice(int, int)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-29 09:44:06 +08:00
qiufengshe
48bb8e91de
perf: minimize temporary strings for better performance (#1255) 2025-04-29 09:33:14 +08:00
leo
53a55467f1
enhance: ignore submodule changes when deal with local changes before pull/checkout/create branch (#1256)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-29 09:27:09 +08:00
leo
5681bf489d
fix: empty dialog when generating commit message with AI (#1257)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-29 09:14:24 +08:00
leo
226bc434f5
ux: make log window resizable (#1253)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-28 16:53:51 +08:00
leo
30d42b11e2
enhance: wait a while after branch changed (#1254)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-28 16:19:40 +08:00
leo
2698427cd0
fix: popup running animation did not update after switch back from another page
Signed-off-by: leo <longshuang@msn.cn>
2025-04-28 11:57:36 +08:00
leo
b4f1f35e67
Merge branch 'master' into develop 2025-04-28 09:17:40 +08:00
leo
92f215d039
Merge branch 'release/v2025.15' 2025-04-28 09:16:44 +08:00
leo
2e1cf76c82
version: Release 2025.15
Signed-off-by: leo <longshuang@msn.cn>
2025-04-28 09:16:37 +08:00
leo
951ea8f088
fix: use subject as context menu item header to fix vertical alignment (#1251)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-27 11:20:44 +08:00
Chiahong
21cb87cec5
localization: update zh_TW.axaml (#1249) 2025-04-27 09:38:28 +08:00
github-actions[bot]
f39048df77 doc: Update translation status and sort locale files 2025-04-27 01:38:20 +00:00
AquariusStar
bbdeecdcc6
locallization: update russian translate (#1248) 2025-04-27 09:38:09 +08:00
leo
d2e688908c
ux: use different inline code background for different themes
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 21:26:21 +08:00
leo
91acf0a32a
enhance: fore invalidate measure after data context of BisectStateIndicator changed
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 20:55:11 +08:00
leo
d44d2b9770
ux: use a outer border to hold tooltip of commit message in FileHistories view (#1232)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 18:50:56 +08:00
cdammanintopix
f09367a614
fix: Append UserName to the SourceGitIPCChannel NamedPipeServerStream to allow multiple users usage on the same server (#1244) (#1246) 2025-04-25 16:56:28 +08:00
leo
00e56ce9d1
fix: System.NullReferenceException raised after popup stop (success or not) running
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 15:56:37 +08:00
leo
22d4f26bc3
code_style: run dotnet format
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 14:31:14 +08:00
leo
a94c7f55ce
ux: remove tips in commit list
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 13:37:11 +08:00
leo
1d16925e74
enhance: stop render next inline elements when it is out of bounds
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 13:30:00 +08:00
leo
8c4362a98d
feature: subject presenter supports inline codeblock
Signed-off-by: leo <longshuang@msn.cn>
2025-04-25 13:24:13 +08:00
leo
9efbc7dd7a
localization: update translations for Chinese
Signed-off-by: leo <longshuang@msn.cn>
2025-04-24 10:04:57 +08:00
github-actions[bot]
c519381645 doc: Update translation status and sort locale files 2025-04-24 02:00:26 +00:00
leo
6590812634
localization: update translations for Chinese
Signed-off-by: leo <longshuang@msn.cn>
2025-04-24 10:00:07 +08:00
github-actions[bot]
ad6ed1512b doc: Update translation status and sort locale files 2025-04-24 01:22:43 +00:00
Javier J. Martínez M.
f73e0687a1
localization: update spanish translations (#1241)
add missing translations. `Bisect`/`Bisecting` stays the same because they reference command names.
2025-04-24 09:22:32 +08:00
qiufengshe
ea680782fe
perf: minimize temporary strings for better performance (#1240)
(cherry picked from commit f4dad2bf551ead5640a500297a4a6f408aef1350)
2025-04-23 21:15:58 +08:00
leo
7e282b13fa
code_style: run dotnet format
Signed-off-by: leo <longshuang@msn.cn>
2025-04-23 20:59:39 +08:00
leo
1386ca30e3
fix: typo in conventional commit type (#1239)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-23 20:52:41 +08:00
github-actions[bot]
2107676058 doc: Update translation status and sort locale files 2025-04-23 07:34:37 +00:00
leo
f72f1894c3
feature: supports to enable --ignore-cr-at-eol in diff by default
Signed-off-by: leo <longshuang@msn.cn>
2025-04-23 15:34:21 +08:00
github-actions[bot]
586ff39da1 doc: Update translation status and sort locale files 2025-04-23 02:39:41 +00:00
AquariusStar
9bdbf47522
localization: update russian localization (#1233) 2025-04-23 10:39:30 +08:00
leo
17c08d42a0
enhance: ignore all sub-directories those names start with '.' (#1234)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-23 10:38:01 +08:00
leo
fafa2a53ae
enhance: show commit full message tooltip when hover commit subject in FileHistories view (#1232)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-23 10:33:49 +08:00
leo
7890f7abbf
refactor: use PointerPressed event instead of ListBox.SelectionChanged event to navigate to commit (#1230)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-23 10:17:14 +08:00
leo
345ad06aba
refactor: diff for staged file with --amend enabled (#1231)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 19:20:27 +08:00
leo
78f4809875
fix: no changes were displayed when try to amend a commit without parent (branch first commit) (#1231)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 19:04:40 +08:00
leo
87ebe3741d
fix: for init-commit, app will crash with COMMIT & PUSH due to local branch has not been updated (#1229)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 18:45:14 +08:00
leo
f2000b4a84
enhance: show git log without command itself for git bisect
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 17:51:55 +08:00
leo
9a6c671a96
refactor: --ignore-cr-at-eol is not necessary when --ignore-all-space is enabled
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 16:50:46 +08:00
leo
34e0ea3bcb
enhance: raise bisect error manually
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 16:07:23 +08:00
leo
7be37424e1
fix: modal dialog did not take focus after show (#1225)
Co-authored-by: Gadfly <gadfly@gadfly.vip>
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 16:02:27 +08:00
github-actions[bot]
a42df87b9c doc: Update translation status and sort locale files 2025-04-22 07:45:38 +00:00
leo
df5294bcb7
feature: git bisect support
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 15:45:15 +08:00
leo
9eae1eeb81
ux: add InputGesture for hotkeys dialog
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 11:05:39 +08:00
leo
4c3698b171
feature: use F1 to quick open the Keyboard Shortcuts Reference dialog (#1225)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 10:30:19 +08:00
qiufengshe
9f18cbca5b
minimize temporary strings for better performance (#1224)
* minimize temporary strings for better performance

* minimize temporary strings for better performance

(cherry picked from commit c9e6a8d4c2d7b5fe03ee13af0a79c5334c23b1c0)
2025-04-22 10:23:20 +08:00
leo
6882ae069f
enhance: do not show tooltip if the ower window is deactived (#1218)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-22 10:22:02 +08:00
leo
9c53894cb4
ux: add an empty icon if there are no git command logs available
Signed-off-by: leo <longshuang@msn.cn>
2025-04-21 18:48:01 +08:00
leo
06d033464d
code_style: move commit link parser to Models.CommitLink.Get
Signed-off-by: leo <longshuang@msn.cn>
2025-04-21 17:27:07 +08:00
leo
750ca8ec61
refactor: use custom view locator to create new window/dialog (#1216)
Signed-off-by: leo <longshuang@msn.cn>
(cherry picked from commit 3e6f2b25f15b263e2b84922abc5cf6d621d62a83)
2025-04-21 15:32:21 +08:00
leo
86113701f3
Merge branch 'master' into develop 2025-04-21 09:49:51 +08:00
leo
387b68cdfe
Merge branch 'release/v2025.14' 2025-04-21 09:49:02 +08:00
leo
550c108f84
version: Release 2025.14
Signed-off-by: leo <longshuang@msn.cn>
2025-04-21 09:48:52 +08:00
qiufengshe
232482ca92
minimize temporary strings for better performance (#1215)
(cherry picked from commit b4fa80c0939ca198bff8e858a4dc241efd31d558)
2025-04-21 09:44:26 +08:00
heartacker
b4db88a663
chore: update Avalonia package references to version 11.2.8 (#1220) 2025-04-21 09:43:44 +08:00
leo
41416a6bed
refactor: use DataTemplates instead of create NamedHighlightedTextBlock manually for menu item (#1216)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-20 11:05:24 +08:00
leo
5fd074a9b6
refactor: use view locator instead of creating views manually in viewmodels (#1213)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-19 11:14:19 +08:00
leo
413669741d
enhance: ensure sourcegit_rebase_jobs.json only being used when orig-head and onto are both matched
Signed-off-by: leo <longshuang@msn.cn>
2025-04-18 12:49:19 +08:00
leo
75b4a4b294
enhance: record more git command logs
Signed-off-by: leo <longshuang@msn.cn>
2025-04-18 11:29:59 +08:00
leo
d254b557a9
docs: update README.md
Signed-off-by: leo <longshuang@msn.cn>
2025-04-18 10:25:53 +08:00
leo
892f3b8032
code_style: move SourceGit.CommandExtensions to SourceGit.ViewModels.CommandExtensions
Signed-off-by: leo <longshuang@msn.cn>
2025-04-18 10:24:20 +08:00
leo
afe5d4b969
ux: show an icon to draw user's attention to LOCAL CHANGES when there is an in-progress operation (#1210)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-18 09:59:34 +08:00
lwray-renesas
4d31392085
Fixed tooltip
Tooltip for chosing mine was wrong.
Was --theirs when should be --ours.

(cherry picked from commit 26a471933c)
2025-04-18 09:47:31 +08:00
leo
de31d4bad4
ux: layout of git command log item
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 21:17:54 +08:00
github-actions[bot]
5bd7dd428d doc: Update translation status and sort locale files 2025-04-17 12:04:02 +00:00
leo
4c1a04477e
refactor: enhanced copy commit information context menu (#1209)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 20:03:46 +08:00
leo
090b64d68d
refactor: notification popup uses the same text presenter with git command log viewer (#1149)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 18:21:55 +08:00
leo
231010abc6
ux: custom style for command line in git command log
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 17:45:49 +08:00
leo
3358ff9aee
enhance: disable hyper link and email link in git command logs
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 17:24:56 +08:00
leo
104a3f0bbf
code_style: run dotnet format
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 16:35:18 +08:00
github-actions[bot]
a06d1183d7 doc: Update translation status and sort locale files 2025-04-17 08:32:08 +00:00
leo
9f493abd1a
enhance: add context menu for selected log
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 16:31:30 +08:00
github-actions[bot]
349844cf2a doc: Update translation status and sort locale files 2025-04-17 08:07:57 +00:00
leo
021aab8408
enhance: add a button to clear all git command logs
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 16:07:40 +08:00
leo
c1e31ac4e3
ci: try to remove zlib1g-dev:arm64
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 15:48:02 +08:00
github-actions[bot]
2a43efde07 doc: Update translation status and sort locale files 2025-04-17 05:34:40 +00:00
Javier J. Martínez M.
33ae6a9989
localization: update spanish translations (#1206)
add missing translations
modify instances of `stagear` with `hacer stage`
2025-04-17 13:34:27 +08:00
leo
0e967ffc8e
fix: pressing Alt+Enter to commit and push in a repository that has no remotes will crash (#1205)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 13:30:41 +08:00
github-actions[bot]
c231772298 doc: Update translation status and sort locale files 2025-04-17 05:24:26 +00:00
leo
8b39df32cc
feature: git command logs
Signed-off-by: leo <longshuang@msn.cn>
2025-04-17 13:23:56 +08:00
leo
928a0ad3c5
feature: add wip (work in progress) type (#1200)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-16 19:39:24 +08:00
leo
9606f128e4
enhance: remember commit message when exiting (#1166)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-16 16:36:23 +08:00
leo
67255a5529
ux: reduce combobox item height
Signed-off-by: leo <longshuang@msn.cn>
2025-04-16 15:42:44 +08:00
leo
cac4b7edf6
enhance: conventional commit message helper (#1200)
- add `build`, `ci`, `pref`
- re-design helper dialog

Signed-off-by: leo <longshuang@msn.cn>
2025-04-16 15:40:26 +08:00
leo
fa44fa773c
ux: re-design welcome (repositories manager) page (#1202)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-16 11:54:50 +08:00
leo
db46de0261
enhance: append character U+26D4 to line when \ No newline at end of file is found in git diff output (#1197)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-16 11:41:40 +08:00
leo
e9036b5fb9
ux: re-design commit message input box
Signed-off-by: leo <longshuang@msn.cn>
2025-04-16 10:22:54 +08:00
leo
9ba0b595d9
enhance: remember the last state of Ignore Whitespace Change and EOF in text diff view (#1198)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 17:58:26 +08:00
github-actions[bot]
cf763b47c6 doc: Update translation status and sort locale files 2025-04-15 09:47:30 +00:00
leo
539d3f6eca
ux: re-design commit message input box (#1169)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 17:47:12 +08:00
leo
03216fc31e
code_style: run dotnet format and re-order codes
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 16:30:50 +08:00
leo
3cc463d24b
enhance: use Environment.Exit(0) instead of IClassicDesktopStyleApplicationLifetime.Shutdown to stop for non-first instance of SourceGit
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 16:24:48 +08:00
leo
33a463ce59
feature: allow to view contribution chart based on selected author (#1196)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 16:08:38 +08:00
leo
90b37663ed
refactor: use lock file instead of named mutex since the second one may not work on other platforms
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 14:36:12 +08:00
leo
9ebde1943e
project: downgrade AvaloniaUI to 11.2.6 to fix duplicated characters when input to textbox (#1195)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 11:15:13 +08:00
leo
be3f418680
fix: no diff content shows with new files (#1193)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 11:03:34 +08:00
leo
a97f163860
readme: update translation tips
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 10:53:53 +08:00
github-actions[bot]
c1839199ee doc: Update translation status and sort locale files 2025-04-15 02:42:33 +00:00
leo
7d5ffaf867
code_style: keep all translations ordered by key
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 10:42:16 +08:00
github-actions[bot]
f0d4cfc9f9 doc: Update translation status and sort locale files 2025-04-15 02:30:43 +00:00
Oleksii Borovyk
70494485ab
Added ukrainian translation (#1191)
(cherry picked from commit b40bfeb98f35da080a1b3935e801e422858faf14)
2025-04-15 10:30:24 +08:00
leo
c4c04b8b01
enhance: bring window into view after receive IPC message
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 10:19:57 +08:00
leo
e2da44c8fd
enhance: use Mutex to force running SourceGit in singleton mode
Signed-off-by: leo <longshuang@msn.cn>
2025-04-15 09:35:16 +08:00
leo
0acbe3e487
enhance: use PipeOptions.FirstPipeInstance to create NamedPipeServerStream
Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 23:55:42 +08:00
leo
05982e6dc0
style: re-design style for disabled primary button
Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 23:23:40 +08:00
leo
e5dc211c35
refactor: simpfy IPC code
Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 23:17:04 +08:00
Gadfly
1e0fd63543
localization: add translation sorting and formatting (#1186)
* doc: Update translation status and missing keys

* localization: add translation sorting and formatting

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-04-14 22:07:24 +08:00
Gadfly
3b1018e0e2
fix: update visible staged changes retrieval in WorkingCopy (#1187)
* doc: Update translation status and missing keys

* fix: update visible staged changes retrieval in WorkingCopy

* fix: prevent unintended amend behavior when changing current branch

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-04-14 22:05:05 +08:00
leo
7d20f97f4e
code_review: PR #1185
- make `SourceGit` running in singleton mode
- `TrySendArgsToExistingInstance` should not be called before `BuildAvaloniaApp().StartWithClassicDesktopLifetime(args)` since we may want to launch `SourceGit` as a core editor.
- avoid `preference.json` to be saved by multiple instances.
- move IPC code to models.

Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 22:03:51 +08:00
Massimo
09c0edef8e
feat: implement IPC for opening repositories in new tabs (#1185)
* refactor: improve diff handling for EOL changes and enhance text diff display

- Updated `Diff.cs` to streamline whitespace handling in diff arguments.
- Enhanced `DiffContext.cs` to check for EOL changes when old and new hashes differ, creating a text diff if necessary.
- Added support for showing end-of-line symbols in `TextDiffView.axaml.cs` options.

* localization: update translations to include EOF handling in ignore whitespace messages

- Modified the ignore whitespace text in multiple language files to specify that EOF changes are also ignored.
- Ensured consistency across all localization files for the patch application feature.

* revert: Typo in DiffResult comment

* revert: update diff arguments to ignore CR at EOL in whitespace handling (like before changes)

* revert: update translations to remove EOF references in Text.Apply.IgnoreWS and fixed typo in Text.Diff.IgnoreWhitespace (EOF => EOL)

* feat: add workspace-specific default clone directory functionality

- Implemented logic in Clone.cs to set ParentFolder based on the active workspace's DefaultCloneDir if available, falling back to the global GitDefaultCloneDir.
- Added DefaultCloneDir property to Workspace.cs to store the default clone directory for each workspace.
- Updated ConfigureWorkspace.axaml to include a TextBox and Button for setting the DefaultCloneDir in the UI.
- Implemented folder selection functionality in ConfigureWorkspace.axaml.cs to allow users to choose a directory for cloning.
- This closes issue #1145

* feat: implement IPC for opening repositories in new tabs

- Added functionality to send repository paths to an existing instance of the application using named pipes.
- Introduced a new preference option to open repositories in a new tab instead of a new window.
- Updated UI to include a checkbox for the new preference.
- Enhanced the handling of IPC server lifecycle based on the new preference setting.
- This closes issue #1184

---------

Co-authored-by: mpagani <massimo.pagani@unitec-group.com>
2025-04-14 19:16:15 +08:00
github-actions[bot]
558eb7c9ac doc: Update translation status and missing keys 2025-04-14 09:03:23 +00:00
leo
b7aa49403b
code_review: PR #1183
- code style in `Clone` constructor
- re-design workspace configuration dialog

Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 17:03:08 +08:00
Massimo
f14a666091
feat: add workspace-specific default clone directory functionality (#1183)
* refactor: improve diff handling for EOL changes and enhance text diff display

- Updated `Diff.cs` to streamline whitespace handling in diff arguments.
- Enhanced `DiffContext.cs` to check for EOL changes when old and new hashes differ, creating a text diff if necessary.
- Added support for showing end-of-line symbols in `TextDiffView.axaml.cs` options.

* localization: update translations to include EOF handling in ignore whitespace messages

- Modified the ignore whitespace text in multiple language files to specify that EOF changes are also ignored.
- Ensured consistency across all localization files for the patch application feature.

* revert: Typo in DiffResult comment

* revert: update diff arguments to ignore CR at EOL in whitespace handling (like before changes)

* revert: update translations to remove EOF references in Text.Apply.IgnoreWS and fixed typo in Text.Diff.IgnoreWhitespace (EOF => EOL)

* feat: add workspace-specific default clone directory functionality

- Implemented logic in Clone.cs to set ParentFolder based on the active workspace's DefaultCloneDir if available, falling back to the global GitDefaultCloneDir.
- Added DefaultCloneDir property to Workspace.cs to store the default clone directory for each workspace.
- Updated ConfigureWorkspace.axaml to include a TextBox and Button for setting the DefaultCloneDir in the UI.
- Implemented folder selection functionality in ConfigureWorkspace.axaml.cs to allow users to choose a directory for cloning.
- This closes issue #1145

---------

Co-authored-by: mpagani <massimo.pagani@unitec-group.com>
2025-04-14 16:41:34 +08:00
leo
e89dbd8f43
code_review: PR #1177
- use `Command.ReadToEnd` instead of `Command.Exec` to avoid git trims line endings.
- use `StringBuilder.Append('\n')` instead of `StringBuilder.AppendLine()` to restore original line endings (we split the original diff output by `\n` not `\r')
- there's no need to show file content (the `StreamReader.ReadLine()` will trim line endings)

Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 16:06:52 +08:00
Massimo
81820e7034
refactor: improve diff handling for EOL changes and enhance text diff… (#1177)
* refactor: improve diff handling for EOL changes and enhance text diff display

- Updated `Diff.cs` to streamline whitespace handling in diff arguments.
- Enhanced `DiffContext.cs` to check for EOL changes when old and new hashes differ, creating a text diff if necessary.
- Added support for showing end-of-line symbols in `TextDiffView.axaml.cs` options.

* localization: update translations to include EOF handling in ignore whitespace messages

- Modified the ignore whitespace text in multiple language files to specify that EOF changes are also ignored.
- Ensured consistency across all localization files for the patch application feature.

* revert: Typo in DiffResult comment

* revert: update diff arguments to ignore CR at EOL in whitespace handling (like before changes)

* revert: update translations to remove EOF references in Text.Apply.IgnoreWS and fixed typo in Text.Diff.IgnoreWhitespace (EOF => EOL)

---------

Co-authored-by: mpagani <massimo.pagani@unitec-group.com>
2025-04-14 15:18:45 +08:00
leo
e7f0217a7b
refactor: move binding for ToolTip.IsOpen from code to axaml
Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 11:51:40 +08:00
leo
0cb2ca78fe
code_review: PR #1180
- replace `SetNeedNavigateToUpstreamHead` with `NavigateToBranchDelayed`
- navigate to current HEAD instead of original source HEAD after merge

Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 11:35:50 +08:00
Gadfly
17cf402c78
enhance: navigate to upstream head after fetch, pull, and merge (#1180) 2025-04-14 10:42:34 +08:00
leo
245de9b458
fix: tooltip did not hide after pointer move out (#1178)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 10:40:24 +08:00
leo
e76328ff38
Merge branch 'master' into develop 2025-04-14 09:56:48 +08:00
leo
61a1b130f2
Merge branch 'release/v2025.13' 2025-04-14 09:55:55 +08:00
leo
69d8d963ea
version: Release 2025.13
Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 09:55:49 +08:00
leo
48835ca043
project: upgrade AvaloniaUI to 11.2.7
Signed-off-by: leo <longshuang@msn.cn>
2025-04-14 09:53:59 +08:00
github-actions[bot]
241f92a290 doc: Update translation status and missing keys 2025-04-14 01:47:39 +00:00
AquariusStar
12b1204809
update russian localization (#1181) 2025-04-14 09:47:23 +08:00
leo
cd5a682194
refactor: directly use --exclude=HEAD instead of --exclude=HEA[D]
Signed-off-by: leo <longshuang@msn.cn>
2025-04-11 15:44:02 +08:00
leo
18888de081
refactor: rewrite histories filter to support ref name that contains non-ascii characters (#1175)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-11 15:39:51 +08:00
github-actions[bot]
13af0a43ed doc: Update translation status and missing keys 2025-04-11 04:31:25 +00:00
leo
af350c2fcd
code_review: PR #1174
- keeps all keys in locale files in order
- add தமிழ் (Tamil)

Signed-off-by: leo <longshuang@msn.cn>
2025-04-11 12:31:09 +08:00
தமிழ் நேரம்
accccb5ea3
தமிழ் (Tamil) translation file added (#1174)
* Tamil file added

220

* 0

* Tamil file added

220

0

(cherry picked from commit 355db34db382d17e0b8f0bb6f05b8e2e2f2963d9)
2025-04-11 12:22:48 +08:00
github-actions[bot]
cfabfb7368 doc: Update translation status and missing keys 2025-04-11 02:02:56 +00:00
leo
1799de4907
code_review: PR #1173
- rename c-style `file_arg` to `fileArg`
- add missing translations for zh_CN and zh_TW
- re-design conflict view and add tooltip for `USE THEIRS` and `USE MINE`
- re-order unstaged toolbar buttons

Signed-off-by: leo <longshuang@msn.cn>
2025-04-11 10:02:33 +08:00
github-actions[bot]
a99ab37797 doc: Update translation status and missing keys 2025-04-11 01:33:18 +00:00
Göran W
47824dc27a
Add button for running external mergetool on ALL conflicts (#1173)
* Make a few non-translated strings localizable (in Conflict view)

* Add button and wiring to run mergetool on all conflicts

* Corrected spelling and wording in related code and exception msg
2025-04-11 09:33:07 +08:00
leo
3b18ee0b37
code_style: remove unused code
Signed-off-by: leo <longshuang@msn.cn>
2025-04-08 20:20:20 +08:00
github-actions[bot]
5d90c2ed60 doc: Update translation status and missing keys 2025-04-08 12:06:21 +00:00
leo
768b324356
ux: if there are no local changes, show different confirm message (#1143)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-08 20:06:00 +08:00
github-actions[bot]
8b5f491e34 doc: Update translation status and missing keys 2025-04-08 12:01:06 +00:00
leo
506af95963
enhance: new confirm empty commit dialog (#1143)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-08 20:00:42 +08:00
leo
898a8bc69a
ux: resize confirm commit dialog
Signed-off-by: leo <longshuang@msn.cn>
2025-04-08 18:05:53 +08:00
leo
da38b72ee5
ux: disable commit button when commit message is empty
Signed-off-by: leo <longshuang@msn.cn>
2025-04-08 18:03:40 +08:00
leo
7cda7211f1
refactor: statistics dialog
- use `%aN+%aE` instead of `%aN` to get commit author
- show user avatar in statistics dialog

Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 21:20:59 +08:00
Göran W
1555abd027
Added new ExternalMerger - Plastic SCM (#1162)
Motivation:
https://m-pixel.com/how-to-use-plastic-scms-merge-tool-with-p4v/
2025-04-07 20:22:53 +08:00
github-actions[bot]
7fedef396f doc: Update translation status and missing keys 2025-04-07 12:19:19 +00:00
leo
2c5ee4fa99
localization: add keys deleted by sorter tools back
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 20:19:00 +08:00
github-actions[bot]
f29402ceec doc: Update translation status and missing keys 2025-04-07 12:05:43 +00:00
leo
f5c213060e
localization: keep all keys in order and remove duplicated keys in fr_FR (#1161)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 20:05:24 +08:00
leo
3275dd07d2
enhance: auto stash and re-apply local changes before squashing (#1141)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 16:04:10 +08:00
github-actions[bot]
67fb0b300f doc: Update translation status and missing keys 2025-04-07 06:43:08 +00:00
leo
3049730dd5
feature: add Preferred Merge Mode in repository configure (#1156)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 14:42:46 +08:00
leo
ad9021e892
enhance: allow using + character in branch name (#1152)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 14:07:58 +08:00
leo
39f7f119dd
doc: always show current translation status in develop branch
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 12:06:06 +08:00
leo
3431ed4bab
Merge branch 'master' into develop 2025-04-07 12:02:24 +08:00
leo
f3d99d64bf
Merge branch 'release/v2025.12' 2025-04-07 12:00:59 +08:00
leo
b65c697e5b
version: Release 2025.12
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 12:00:45 +08:00
github-actions[bot]
48f8b6116a doc: Update translation status and missing keys 2025-04-07 03:48:59 +00:00
leo
fa02c65da5
code_review: PR #1153
- use a single filter for both unstage and staged files
- show confirm dialog if staged files are displayed partially

Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 11:48:38 +08:00
Göran W
a37c6b29ec
In Local Changes, added filter-box in Staged area, to match Unstaged area (#1153)
Also added minimal handling (RaiseException) if trying to commit with active filter (might commit more changes than visible, so disallow).
Minor unification in unstageChanges() to make it more similar to StageChanges().
2025-04-07 10:37:58 +08:00
leo
ac7b02590b
enhance: add comma between date and time (#1150)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 10:23:37 +08:00
leo
8c9cf05c1d
fix: renamed files are missing in commit changes and stash changes (#1151)
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 10:14:02 +08:00
leo
c615d04038
doc: update README.md for Japanese support
Signed-off-by: leo <longshuang@msn.cn>
2025-04-07 09:54:48 +08:00
github-actions[bot]
17d285d9bf doc: Update translation status and missing keys 2025-04-07 01:53:35 +00:00
Sousi Omine
ef106e6909
Add Japanese localization (#1157)
* Initial Japanese translation

Only a small part was translated

* Unspecified words will be in English

When new words are added, they will be displayed in English even if Japanese support is delayed.

* Expanded translation scope

* Expanded translation scope

* Proceed with translation with a focus on overall settings

* Re-translated the outdated settings screen

* Add items that only exist in the latest en_US and remove items that do not exist in en_US

* A lot of translation work done

* A lot more translation work has been done

* ja_JP.axaml has been translated into Japanese

* Fixed three incomplete parts of the Japanese translation
2025-04-07 09:53:20 +08:00
github-actions[bot]
cbc7079e59 doc: Update translation status and missing keys 2025-04-07 01:45:21 +00:00
UchiTesting
904432a8f1
style(locale): Add a few translations to the French locale (#1158) 2025-04-07 09:45:02 +08:00
github-actions[bot]
7ef4cca1f5 doc: Update translation status and missing keys 2025-04-02 09:20:43 +00:00
Javier J. Martínez M.
2deb79f8ce
localization: update spanish translations (#1142)
add literal translation for 'CopyFullPath' string
2025-04-02 17:20:33 +08:00
leo
8e55ba1b47
enhance: avoid unhandled exceptions in timer
Signed-off-by: leo <longshuang@msn.cn>
2025-03-31 19:06:10 +08:00
leo
55be1ad1ca
Merge branch 'master' into develop 2025-03-31 09:30:56 +08:00
leo
1138ba304d
Merge branch 'release/2025.11' 2025-03-31 09:30:18 +08:00
leo
ae5fa6a793
version: Release 2025.11
Signed-off-by: leo <longshuang@msn.cn>
2025-03-31 09:29:56 +08:00
leo
0045e06d78
project: upgrade AvaloniaUI to 11.2.6
Signed-off-by: leo <longshuang@msn.cn>
2025-03-31 09:29:07 +08:00
qiufengshe
07d99f5fd2
enhance: get email hash code opimization (#1137)
(cherry picked from commit 839b92a284d6b103894f6a8a39e5ce1f99bb12fa)
2025-03-31 09:22:57 +08:00
github-actions[bot]
9ee3a00fba doc: Update translation status and missing keys 2025-03-31 01:21:15 +00:00
AquariusStar
1482a005bb
localization: update and fix translation russian (#1136) 2025-03-31 09:20:54 +08:00
github-actions[bot]
ce7196490a doc: Update translation status and missing keys 2025-03-28 10:02:16 +00:00
leo
276d000bcf
refactor: change Copy File Name to Copy Full Path for selected file or change (#1132)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-28 18:01:53 +08:00
github-actions[bot]
b26c8a64ad doc: Update translation status and missing keys 2025-03-28 04:20:55 +00:00
leo
56ebc182f2
enhance: try to reinstate not onl the working tree's change, but also the index's ones (#1135)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-28 12:20:36 +08:00
Gadfly
1575ae977e
fix: improve font family name handling by collapsing multiple spaces (#1131) 2025-03-27 20:22:46 +08:00
Gadfly
4153eec1a8
chore: Update DEB package configuration with installed size (#1130) 2025-03-26 12:15:15 +08:00
github-actions[bot]
fc37677546 doc: Update translation status and missing keys 2025-03-26 01:30:58 +00:00
leo
4fb853d1fd
localization: add translation Text.Configure.IssueTracker.AddSampleAzure for Chinese (#1128)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-26 09:30:41 +08:00
github-actions[bot]
dccf53e518 doc: Update translation status and missing keys 2025-03-26 01:27:21 +00:00
Iacopo Sbalchiero
ca0fb7ae10
Adding template for Azure DevOps workitems (#1128)
* feat: add Azure DevOps issue tracker integration

* localization: add Azure DevOps sample rule to issue tracker in multiple languages
2025-03-26 09:27:10 +08:00
leo
f37ac904b9
enhance: do not create crash log for unobserved task exceptions (#1121)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-25 10:33:39 +08:00
github-actions[bot]
467089aec5 doc: Update translation status and missing keys 2025-03-25 01:51:33 +00:00
Javier J. Martínez M.
380e6713b5
localization: update spanish translations (#1124) 2025-03-25 09:51:22 +08:00
leo
fc85dd3269
enhance: improve Repository.Open() performance (#1121)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-24 19:37:39 +08:00
leo
0a877c6730
project: upgrade OpenAI and Azure.AI.OpenAI to 2.2.0-beta.4
Signed-off-by: leo <longshuang@msn.cn>
2025-03-24 10:03:27 +08:00
leo
166c925eee
Merge branch 'master' into develop 2025-03-24 09:45:30 +08:00
leo
7581d761cc
Merge branch 'release/v2025.10' 2025-03-24 09:44:50 +08:00
leo
88bb603dc9
version: Release 2025.10
Signed-off-by: leo <longshuang@msn.cn>
2025-03-24 09:44:41 +08:00
leo
d335cac167
enhance: only raise BlockNavigationChangedEvent when UseBlockNavigation enabled
Signed-off-by: leo <longshuang@msn.cn>
2025-03-21 18:00:46 +08:00
leo
9590f96a44
enhance: clear highlight chunk while scrolling out of TextArea.TextView
Signed-off-by: leo <longshuang@msn.cn>
2025-03-21 17:46:11 +08:00
leo
03f49ccff0
refactor: text diff view block navigation
Signed-off-by: leo <longshuang@msn.cn>
2025-03-21 17:35:59 +08:00
leo
39f4cd1732
ci: move all translation status to TRANSLATION.md and do not modify README.md while checking localization
Signed-off-by: leo <longshuang@msn.cn>
2025-03-21 10:54:47 +08:00
leo
cdc0fbb753
doc: update README.md
Signed-off-by: leo <longshuang@msn.cn>
2025-03-21 10:23:52 +08:00
leo
8c1e1a3e6a
fix: text diff view scrolling issue introduced by AvaloniaEdit 11.2.0 (commit 7caa03a09b)
- `SyncScrollOffset` does not update in `side-by-side` mode while scrolling
- Highlighted chunk is not cleared when scroll by drag scrollbar

Signed-off-by: leo <longshuang@msn.cn>
2025-03-21 10:09:43 +08:00
github-actions[bot]
56253e95c3 doc: Update translation status and missing keys 2025-03-21 01:31:07 +00:00
Ilian Delagrange
5467703a6e
localization: add missing french translations (#1113)
Co-authored-by: Ilian Delagrange <ilian@MacBook-Air-de-Ilian.local>
2025-03-21 09:30:57 +08:00
leo
7cd5814410
enhance: better regex for output of Commands.CompareRevisions
Signed-off-by: leo <longshuang@msn.cn>
2025-03-20 21:18:51 +08:00
leo
38d87fa1a1
feature: use git stash show -u --name-status <stash_name> command to query changes in selected stash if git >= 2.32.0
Signed-off-by: leo <longshuang@msn.cn>
2025-03-20 21:12:08 +08:00
leo
65dbfd336d
refactor: it's not necessary to store untracked file list for selected stash
Signed-off-by: leo <longshuang@msn.cn>
2025-03-20 20:53:30 +08:00
leo
891e1b2ec8
ux: show name of stash instead of SHA which is useless to user
Signed-off-by: leo <longshuang@msn.cn>
2025-03-20 19:56:06 +08:00
github-actions[bot]
c349ac10f3 doc: Update translation status and missing keys 2025-03-20 03:11:14 +00:00
leo
145273b4a7
refactor: move Show tags in commit graph to Preferences (#1109)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-20 11:10:48 +08:00
leo
a5bdcab341
code_style: move dynamic context menu creation to view models
Signed-off-by: leo <longshuang@msn.cn>
2025-03-20 09:38:02 +08:00
github-actions[bot]
673b335a2a doc: Update translation status and missing keys 2025-03-20 01:14:21 +00:00
AquariusStar
f7197e08eb
localization: update russian localization (#1111) 2025-03-20 09:14:07 +08:00
github-actions[bot]
f02a7b9858 doc: Update translation status and missing keys 2025-03-18 13:40:50 +00:00
leo
2512d3be7a
feature: allow to hide tags in graph (#1109)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-18 21:40:31 +08:00
leo
ae1e46b586
fix: layout horizontal not working since 2025.9 after switching away from history screen (#1108)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-18 19:47:54 +08:00
leo
822452a20c
enhance: show inner exception message if possible when check update failed
Signed-off-by: leo <longshuang@msn.cn>
2025-03-18 15:55:32 +08:00
leo
760e44877b
fix: wrong split char
Signed-off-by: leo <longshuang@msn.cn>
2025-03-18 12:15:00 +08:00
leo
695db2a319
code_style: run dotnet format
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 20:57:18 +08:00
leo
8d47bd5cd9
refactor: use --format=<format> instead of --pretty=format:<format>
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 20:46:01 +08:00
leo
808302ce84
code_review: PR #1106
- Use new syntex `[...]` instead of `new char[] {...}` to create char arrays
- Use `string.ReplaceLineEndings('\n').Split('\n')` instead of `string.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries)` because that the `Signer` part may be missing

Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 20:35:31 +08:00
Gadfly
b930066b5a
fix: improve line splitting to handle both LF and CRLF line endings (#1106) 2025-03-17 19:59:28 +08:00
leo
a0cddaea80
feature: support --ff-only option for git merge command
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 19:53:47 +08:00
github-actions[bot]
2b95ea2ab1 doc: Update translation status and missing keys 2025-03-17 09:15:30 +00:00
Leonardo
c9fe373dda
add missing key and fix untranslated one (#1104) 2025-03-17 17:15:17 +08:00
leo
398b14695c
enhance: the git dir of worktree's owner repository may not named .git
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 17:10:59 +08:00
github-actions[bot]
5845ef3eb6 doc: Update translation status and missing keys 2025-03-17 09:06:42 +00:00
leo
3e8bba0d0b
ux: re-design About page
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 17:06:26 +08:00
leo
7031693489
refactor: pass dirs to watcher directly
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 16:30:16 +08:00
leo
b4ab4afd3a
code_review: PR #1103
Since we only use `$GIT_COMMON_DIR` in filesystem watcher, it is unnecessary to store this value in `Repository`, and we can query the `$GIT_COMMON_DIR` only when it looks like a worktree

Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 16:19:59 +08:00
Gadfly
cea8a90680
refactor: use $GIT_COMMON_DIR instead of cut $GIT_DIR/worktrees (#1103) 2025-03-17 15:56:13 +08:00
github-actions[bot]
265aaa1d67 doc: Update translation status and missing keys 2025-03-17 07:30:48 +00:00
leo
cdd1926e2f
refactor: rewrite git apply implementation
- Do not translate commandline options for `git`
- Re-design combox layout for `git apply` popup

Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 15:30:32 +08:00
leo
ddfc868df3
ux: re-design merge option style
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 15:08:49 +08:00
leo
10fd0f9d15
doc: fix typo
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 12:02:32 +08:00
leo
99a45335fe
doc: group third-party components by types
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 12:01:29 +08:00
leo
8f8385072c
doc: add third-party components
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 11:56:38 +08:00
Gadfly
a480ba0139
docs: Add third-party components and licenses section
(cherry picked from commit 95c697248755f7b6de4d2d0bfdaa9de1e572c9f6)
2025-03-17 11:47:51 +08:00
Gadfly
4b41029768
fix: use better JSP grammar file and add licensing information (#1102) 2025-03-17 11:31:50 +08:00
leo
6273c01d71
fix: git rev-list raises errors after selected commit in Histories page (#1101)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 10:55:54 +08:00
Gadfly
450dadf76c
feat: Add JSP grammar support and improve TextMateHelper file type handling (#1100)
- Extend grammar support by allowing multiple file extensions per grammar and adding JSP file type handling.
- Add a new JSON grammar file for JavaServer Pages (JSP) syntax highlighting.
2025-03-17 10:12:44 +08:00
leo
7caa03a09b
project: upgrade AvaloniaEdit to 11.2.0
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 09:57:52 +08:00
leo
d9cf849b9f
Merge branch 'master' into develop 2025-03-17 09:38:30 +08:00
leo
6fd6bbb6b5
Merge branch 'release/v2025.09' 2025-03-17 09:37:49 +08:00
leo
34f8618989
version: Release 2025.09
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 09:37:40 +08:00
leo
84979b20b3
ux: force using VertialAlignment="Center" for sign info of commit (#1098)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-17 09:32:31 +08:00
Gadfly
66517fd4bf
enhance: add tooltips to various UI elements for better accessibility (#1097)
* enhance: add tooltips to various UI elements for better accessibility

* refactor: simplify user string conversion
2025-03-16 11:23:42 +08:00
github-actions[bot]
a46e52582f doc: Update translation status and missing keys 2025-03-14 08:57:15 +00:00
Asurada
db504241ea
feat: add translation for "1 hour ago" in multiple languages (#1096) 2025-03-14 16:57:03 +08:00
leo
c3e1fb93b6
refactor: fix maxOS PATH env
Signed-off-by: leo <longshuang@msn.cn>
2025-03-14 10:54:09 +08:00
leo
c8bee2f6ba
code_review: PR #1093
Merge deleting branch and tag on remote into `SourceGit.Commands.Push(repo, remote, refname, isDelete)`

Signed-off-by: leo <longshuang@msn.cn>
2025-03-14 09:36:34 +08:00
Michael Pakhantsov
9645b65db6
Explicitly provided fully qualified reference for the git branch, becase can be exists a tag and a branch with identical names (#1093)
Fix push command for branch deletion

Updated the `push` command to use `--delete refs/heads/{name}` instead of `--delete {name}` for clearer branch reference when deleting a remote branch.

Co-authored-by: Michael Pakhantsov <michae.pakhantsov@gmail.com>
2025-03-14 09:26:59 +08:00
leo
67f4330dd4
code_style: arrange methods in App.axaml.cs
Signed-off-by: leo <longshuang@msn.cn>
2025-03-13 15:23:43 +08:00
leo
9560496c7b
code_review: PR #1092
- Remove `SourceGit.ViewModels.Preference.FixFontFamilyName` (it is not necessary any more)
- Use `string.Join` instead of `StringBuilder` to make the logic more clear

Signed-off-by: leo <longshuang@msn.cn>
2025-03-13 15:17:20 +08:00
Gadfly
b9b684a83d
fix: improve font string processing in SetFonts method (#1092) 2025-03-13 15:05:30 +08:00
github-actions[bot]
519bdf1ddc doc: Update translation status and missing keys 2025-03-13 02:22:09 +00:00
leo
0e261cffd2
refactor: rewrite the way to deal with uncommitted local changes when checkout/pull/create branch (#1085)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-13 10:21:54 +08:00
leo
e430e847ff
enhance: auto convert spaces with dashes while renaming a branch (#1088)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-13 09:50:42 +08:00
Gadfly
7331167be2
fix: schedule DWM frame extension to next render frame on Windows 10 (#1087)
The DwmExtendFrameIntoClientArea call needs to be posted to the next render frame to ensure the window handle is fully initialized and avoid potential race conditions.
2025-03-13 09:38:08 +08:00
Morgan Courbet
f07832c385
docs: fix typo in README.md
(cherry picked from commit 59fd2aaab1)
2025-03-13 09:24:43 +08:00
leo
e4f5c34e0c
code_style: remove whitespaces
Signed-off-by: leo <longshuang@msn.cn>
2025-03-12 17:58:57 +08:00
leo
eaa322dfab
enhance: re-design commit search result display (#1083)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-12 17:57:31 +08:00
leo
77d8afe056
refactor: reduce the times to call RefreshLayout
Signed-off-by: leo <longshuang@msn.cn>
2025-03-12 15:10:43 +08:00
leo
0476a825ef
code_style: move some code from Histories.axaml to separate files
Signed-off-by: leo <longshuang@msn.cn>
2025-03-12 14:58:59 +08:00
leo
bb2284c4c9
refactor: re-write commit searching (part 3)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-12 11:53:24 +08:00
leo
ee7ccc0391
refactor: re-write commit searching (part 2)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-12 11:05:19 +08:00
leo
231f3bf668
enhance: use --ancestry-path=<commit_oid> to reduce unnecessary outpus while querying children commits
Signed-off-by: leo <longshuang@msn.cn>
2025-03-12 10:11:00 +08:00
leo
f5d6e1264d
refactor: use List<T> instead of AvaloniaList<T> since it is not used for bindings
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 23:01:34 +08:00
leo
64a41dce39
refactor: rewrite searching commit by file path
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 20:55:39 +08:00
leo
fa4d9d24e9
enhance: hide suggestion popups when window is deactived (#963)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 20:22:32 +08:00
leo
91c5c96afc
fix: accessing dummy in multi-threads throws exception
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 20:05:34 +08:00
leo
2fc03025ee
fix: file suggestion popup did not show while searching commit by file path
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 19:52:50 +08:00
leo
471452646b
refactor: use System.Threading.CancellationToken instead of SourceGit.Commands.Command.CancelToken to cancel fetching information of selected commit
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 16:53:51 +08:00
leo
f23e3478e6
fix: do not save preference in design mode
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 11:41:37 +08:00
leo
54d49a9eda
fix: app crashes when close a repository on read-only drive (#1080)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 09:36:13 +08:00
leo
5f2bd8ad94
ux: layout for Preferences window
Signed-off-by: leo <longshuang@msn.cn>
2025-03-11 09:27:32 +08:00
leo
f54c8877d7
Merge pull request #1079 from AquariusStar/develop_translation_russian
update russian localization
2025-03-11 09:21:20 +08:00
Михаил Усоцкий
f496d15f70 update russian localization 2025-03-10 20:15:29 +03:00
leo
cf8cff6b64
code_style: add ViewModels.Repository.GetCustomAction(scope)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 21:30:04 +08:00
leo
5c279b4b56
feature: add global configuration for custom action (#1077)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 21:19:38 +08:00
leo
0f9087fac6
code_review: PR #1078
- Remove `ForceEnglishLocale` because we want all `git` outputs in English
- Remove locale settings for `ExecuteCustomAction`

Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 20:08:42 +08:00
Gadfly
860f52153b
fix: Force English locale in branch query command. (#1078) 2025-03-10 20:05:37 +08:00
leo
b4fbc2372b
enhance: show commit info tip when hover SHA in conflict view
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 20:02:09 +08:00
leo
2b2f070c4a
enhance: use --no-optional-locks parameter for git status command
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 18:29:11 +08:00
leo
e65cb50495
fix: use \ as path delim on Windows when executing custom actions (#1077)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 17:48:02 +08:00
Gadfly
cdae9168ed
ci(windows): maintain SourceGit folder structure (#1076) 2025-03-10 15:44:39 +08:00
leo
2e51e8939b
Merge branch 'master' into develop 2025-03-10 12:34:34 +08:00
leo
e3cc987682
Merge branch 'develop' 2025-03-10 12:33:20 +08:00
Gadfly
c1c0e7b2a0
enhance: Save Preferences after switched Workspace and closed Preferences (#1073) 2025-03-10 12:04:19 +08:00
leo
df6cbcaa28
fix: app crashed after enable Show children in the commit details (#1072)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 11:33:38 +08:00
leo
855466686d
ux: show conflict sources when it comes from a stash or patch (#1067)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 10:22:49 +08:00
leo
bfc3f37e47
Merge branch 'master' into develop 2025-03-10 09:37:40 +08:00
leo
18d3b9560b
Merge branch 'release/v2025.08' 2025-03-10 09:36:44 +08:00
leo
82fdd2e9d4
version: Release 2025.08
Signed-off-by: leo <longshuang@msn.cn>
2025-03-10 09:36:36 +08:00
Gadfly
6ba26770c4
ci: enhance Linux and Windows package workflow (#1071)
* ci: fix Linux packaging workflow with specific Ubuntu 20.04 image

* ci: use Compress-Archive in Windows try to prevent Defender false positives
2025-03-10 09:28:13 +08:00
github-actions[bot]
978801c9ff doc: Update translation status and missing keys 2025-03-10 01:27:39 +00:00
AquariusStar
2fb1d7e14a
update and fix translation russian (#1070) 2025-03-10 09:27:25 +08:00
Gustav Andersson
89f655c84d
fix: Show detached HEAD (#1060) 2025-03-07 16:16:15 +08:00
leo
0860245674
code_style: simple window do not using DataContext
Signed-off-by: leo <longshuang@msn.cn>
2025-03-07 15:44:50 +08:00
leo
43fed8e04d
refactor: re-write About window
Signed-off-by: leo <longshuang@msn.cn>
2025-03-07 15:15:47 +08:00
leo
83f23583be
enhance: supports to navigate to target commit while resolving conflicts
Signed-off-by: leo <longshuang@msn.cn>
2025-03-07 12:22:47 +08:00
leo
ca6d41ee60
enhance: try to get stopped at revision info from .git/rebase-merge/head
Signed-off-by: leo <longshuang@msn.cn>
2025-03-07 11:39:15 +08:00
github-actions[bot]
773e27fda7 doc: Update translation status and missing keys 2025-03-07 01:32:14 +00:00
Javier J. Martínez M.
14877d4d63
localization: update spanish translations (#1061)
add missing spanish translations
update previous references of `patch` as `parche`
2025-03-07 09:32:05 +08:00
leo
92fa25ae8d
ux: disable scrollbar in NumericUpDown control (#1023)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-06 15:14:05 +08:00
leo
aa0d4b4296
ux: adjust column width of commit hash and time after font size changed (#994)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-06 11:50:22 +08:00
leo
78c0d8d334
fix: both --tags and --no-tags are used in git pull command
Signed-off-by: leo <longshuang@msn.cn>
2025-03-06 10:37:50 +08:00
leo
81bbe11345
code_style: rename some variables in ParseLinksInMessage
Signed-off-by: leo <longshuang@msn.cn>
2025-03-05 17:06:45 +08:00
Gadfly
991ebe4082
ci: set ubuntu:20.04 (#1056) 2025-03-05 09:55:55 +08:00
leo
fb8d4a2542
code_review: PR #1055
- since the author and time are already shown in the blame view, use the full message as tooltip is better.
- cache the commit message in `ViewModels.Blame` since the `Tooltip` of control may change.
- querying commit message synchronously (it's very fast) to avoid similar issues in commit details panel.

Signed-off-by: leo <longshuang@msn.cn>
2025-03-05 09:54:23 +08:00
GadflyFang
269903503f
feat: Add commit Tooltip in Blame (#1055)
Fixes #993
2025-03-05 09:36:32 +08:00
leo
5e898a809e
enhance: check commit hash after intersect testing
Signed-off-by: leo <longshuang@msn.cn>
2025-03-05 09:30:55 +08:00
GadflyFang
71d0b69eee
fix: prevent kill apt process by accident (#1054) 2025-03-04 19:51:36 +08:00
leo
792e61b24f
ux: re-design the layout for Interactive Rebase window that tries to fix issue #1037
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 18:23:13 +08:00
leo
e884f27f67
code_style: re-order CommitDetail properties
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 18:02:29 +08:00
leo
e28f8611ef
ux: re-design the layout for Interactive Rebase window that tries to fix issue #1037
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 17:39:21 +08:00
github-actions[bot]
5af856b9da doc: Update translation status and missing keys 2025-03-04 09:26:48 +00:00
leo
2137ad9ec9
enhance: allow to configure editor tab width in preferences window (#1048)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 17:26:27 +08:00
leo
11af5d9b29
code_style: use ?: operator instead of if ... else statement
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 16:42:32 +08:00
GadflyFang
68e96f428e
fix: validate result not update #1052 (#1053) 2025-03-04 16:34:51 +08:00
GadflyFang
25e6e261a6
refactor: Improve key modifier checks and AltGr detection (#1051) 2025-03-04 16:33:43 +08:00
leo
96538b9a62
fix: there's an extra line-ending while copy multiple lines from text diff view (#1049)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 16:26:14 +08:00
leo
b75676a7f8
refactor: commit message
- move issue tracker and commit hash links parsing to view models
- parsing links async
- make sure matched hash is a valid commit oid
- disable `CHILDREN` row in submodule info panel

Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 16:04:19 +08:00
leo
5301a368e0
ux: re-design the layout for Interactive Rebase window that tries to fix issue #1037
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 09:46:02 +08:00
leo
d4bcc60113
enhance: disable CONTINUE button while it is running (#1046)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-04 09:29:07 +08:00
leo
35ee4a47db
fix: the last selected line is missing while trying to copy multiple lines in text diff view (#1044)
Signed-off-by: leo <longshuang@msn.cn>
2025-03-03 15:39:59 +08:00
leo
598bba5210
code_review: PR #1043
`FileModeChange` should not be changed out of UIThread

Signed-off-by: leo <longshuang@msn.cn>
2025-03-03 14:25:49 +08:00
GadflyFang
72c7f24e2f
fix: set FileModeChange after RefreshAll (#1043) 2025-03-03 14:17:03 +08:00
GadflyFang
9dffd55c2b
build: Add pre-install and pre-removal scripts for DEBIAN package (#1041)
kill sourcegit before install
2025-03-03 10:39:57 +08:00
GadflyFang
7f8372f6b5
enhance: Handle file mode changes for new/deleted file (#1040) 2025-03-03 10:38:58 +08:00
leo
4c63c4c90d
Merge branch 'master' into develop 2025-03-03 09:28:02 +08:00
430 changed files with 18327 additions and 8319 deletions

View file

@ -71,20 +71,20 @@ dotnet_style_predefined_type_for_member_access = true:suggestion
# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# private static fields should have s_ prefix
dotnet_naming_rule.private_static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
dotnet_naming_rule.private_static_fields_should_have_prefix.style = private_static_prefix_style
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_fields.required_modifiers = static
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private
@ -93,14 +93,14 @@ dotnet_naming_style.private_static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# use accessibility modifiers
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion

View file

@ -19,14 +19,25 @@ jobs:
os: macos-latest
runtime: osx-arm64
- name : Linux
os: ubuntu-20.04
os: ubuntu-latest
runtime: linux-x64
container: ubuntu:20.04
- name : Linux (arm64)
os: ubuntu-20.04
os: ubuntu-latest
runtime: linux-arm64
container: ubuntu:20.04
name: Build ${{ matrix.name }}
runs-on: ${{ matrix.os }}
container: ${{ matrix.container || '' }}
steps:
- name: Install common CLI tools
if: ${{ startsWith(matrix.runtime, 'linux-') }}
run: |
export DEBIAN_FRONTEND=noninteractive
ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime
apt-get update
apt-get install -y sudo
sudo apt-get install -y curl wget git unzip zip libicu66 tzdata clang
- name: Checkout sources
uses: actions/checkout@v4
- name: Setup .NET
@ -47,7 +58,7 @@ jobs:
if: ${{ matrix.runtime == 'linux-arm64' }}
run: |
sudo apt-get update
sudo apt-get install clang llvm gcc-aarch64-linux-gnu zlib1g-dev:arm64
sudo apt-get install -y llvm gcc-aarch64-linux-gnu
- name: Build
run: dotnet build -c Release
- name: Publish

View file

@ -4,7 +4,6 @@ on:
branches: [ develop ]
paths:
- 'src/Resources/Locales/**'
- 'README.md'
workflow_dispatch:
workflow_call:
@ -32,8 +31,8 @@ jobs:
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
if [ -n "$(git status --porcelain)" ]; then
git add README.md TRANSLATION.md
git commit -m 'doc: Update translation status and missing keys'
git add TRANSLATION.md src/Resources/Locales/*.axaml
git commit -m 'doc: Update translation status and sort locale files'
git push
else
echo "No changes to commit"

View file

@ -9,10 +9,10 @@ on:
jobs:
windows:
name: Package Windows
runs-on: ubuntu-latest
runs-on: windows-2019
strategy:
matrix:
runtime: [win-x64, win-arm64]
runtime: [ win-x64, win-arm64 ]
steps:
- name: Checkout sources
uses: actions/checkout@v4
@ -22,6 +22,7 @@ jobs:
name: sourcegit.${{ matrix.runtime }}
path: build/SourceGit
- name: Package
shell: bash
env:
VERSION: ${{ inputs.version }}
RUNTIME: ${{ matrix.runtime }}
@ -69,6 +70,7 @@ jobs:
linux:
name: Package Linux
runs-on: ubuntu-latest
container: ubuntu:20.04
strategy:
matrix:
runtime: [linux-x64, linux-arm64]
@ -77,9 +79,10 @@ jobs:
uses: actions/checkout@v4
- name: Download package dependencies
run: |
sudo add-apt-repository universe
sudo apt-get update
sudo apt-get install desktop-file-utils rpm libfuse2
export DEBIAN_FRONTEND=noninteractive
ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime
apt-get update
apt-get install -y curl wget git dpkg-dev fakeroot tzdata zip unzip desktop-file-utils rpm libfuse2 file build-essential binutils
- name: Download build
uses: actions/download-artifact@v4
with:
@ -89,6 +92,7 @@ jobs:
env:
VERSION: ${{ inputs.version }}
RUNTIME: ${{ matrix.runtime }}
APPIMAGE_EXTRACT_AND_RUN: 1
run: |
mkdir build/SourceGit
tar -xf "build/sourcegit.${{ matrix.runtime }}.tar" -C build/SourceGit

2
.gitignore vendored
View file

@ -37,3 +37,5 @@ build/*.deb
build/*.rpm
build/*.AppImage
SourceGit.app/
build.command
src/Properties/launchSettings.json

View file

@ -11,15 +11,15 @@
* Supports Windows/macOS/Linux
* Opensource/Free
* Fast
* Deutsch/English/Español/Français/Italiano/Português/Русский/简体中文/繁體中文
* Deutsch/English/Español/Français/Italiano/Português/Русский/Українська/简体中文/繁體中文/日本語/தமிழ் (Tamil)
* Built-in light/dark themes
* Customize theme
* Visual commit graph
* Supports SSH access with each remote
* GIT commands with GUI
* Clone/Fetch/Pull/Push...
* Merge/Rebase/Reset/Revert/Amend/Cherry-pick...
* Amend/Reword
* Merge/Rebase/Reset/Revert/Cherry-pick...
* Amend/Reword/Squash
* Interactive rebase
* Branches
* Remotes
@ -35,11 +35,14 @@
* Revision Diffs
* Branch Diff
* Image Diff - Side-By-Side/Swipe/Blend
* Git command logs
* Search commits
* GitFlow
* Git LFS
* Bisect
* Issue Link
* Workspace
* Custom Action
* Using AI to generate commit message (C# port of [anjerodev/commitollama](https://github.com/anjerodev/commitollama))
> [!WARNING]
@ -47,16 +50,13 @@
## Translation Status
[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.21%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-99.87%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-91.80%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-%E2%88%9A-brightgreen)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-91.53%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.07%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md)
> [!NOTE]
> You can find the missing keys in [TRANSLATION.md](TRANSLATION.md)
You can find the current translation status in [TRANSLATION.md](https://github.com/sourcegit-scm/sourcegit/blob/develop/TRANSLATION.md)
## How to Use
**To use this tool, you need to install Git(>=2.23.0) first.**
**To use this tool, you need to install Git(>=2.25.1) first.**
You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [Github Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits.
You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [GitHub Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits.
This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationData}/SourceGit"`, which is platform-dependent, to store user settings, downloaded avatars and crash logs.
@ -79,7 +79,7 @@ For **Windows** users:
```
> [!NOTE]
> `winget` will install this software as a commandline tool. You need run `SourceGit` from console or `Win+R` at the first time. Then you can add it to the taskbar.
* You can install the latest stable by `scoope` with follow commands:
* You can install the latest stable by `scoop` with follow commands:
```shell
scoop bucket add extras
scoop install sourcegit
@ -93,7 +93,7 @@ For **macOS** users:
brew tap ybeapps/homebrew-sourcegit
brew install --cask --no-quarantine sourcegit
```
* If you want to install `SourceGit.app` from Github Release manually, you need run following command to make sure it works:
* If you want to install `SourceGit.app` from GitHub Release manually, you need run following command to make sure it works:
```shell
sudo xattr -cr /Applications/SourceGit.app
```
@ -201,3 +201,7 @@ dotnet run --project src/SourceGit.csproj
Thanks to all the people who contribute.
[![Contributors](https://contrib.rocks/image?repo=sourcegit-scm/sourcegit&columns=20)](https://github.com/sourcegit-scm/sourcegit/graphs/contributors)
## Third-Party Components
For detailed license information, see [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md).

View file

@ -60,6 +60,8 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DEBIAN", "DEBIAN", "{F101849D-BDB7-40D4-A516-751150C3CCFC}"
ProjectSection(SolutionItems) = preProject
build\resources\deb\DEBIAN\control = build\resources\deb\DEBIAN\control
build\resources\deb\DEBIAN\preinst = build\resources\deb\DEBIAN\preinst
build\resources\deb\DEBIAN\prerm = build\resources\deb\DEBIAN\prerm
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rpm", "rpm", "{9BA0B044-0CC9-46F8-B551-204F149BF45D}"

86
THIRD-PARTY-LICENSES.md Normal file
View file

@ -0,0 +1,86 @@
# Third-Party Licenses
This project incorporates components from the following third parties:
## Packages
### AvaloniaUI
- **Source**: https://github.com/AvaloniaUI/Avalonia
- **Version**: 11.2.5
- **License**: MIT License
- **License Link**: https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md
### AvaloniaEdit
- **Source**: https://github.com/AvaloniaUI/AvaloniaEdit
- **Version**: 11.2.0
- **License**: MIT License
- **License Link**: https://github.com/AvaloniaUI/AvaloniaEdit/blob/master/LICENSE
### LiveChartsCore.SkiaSharpView.Avalonia
- **Source**: https://github.com/beto-rodriguez/LiveCharts2
- **Version**: 2.0.0-rc5.4
- **License**: MIT License
- **License Link**: https://github.com/beto-rodriguez/LiveCharts2/blob/master/LICENSE
### TextMateSharp
- **Source**: https://github.com/danipen/TextMateSharp
- **Version**: 1.0.66
- **License**: MIT License
- **License Link**: https://github.com/danipen/TextMateSharp/blob/master/LICENSE.md
### OpenAI .NET SDK
- **Source**: https://github.com/openai/openai-dotnet
- **Version**: 2.2.0-beta2
- **License**: MIT License
- **License Link**: https://github.com/openai/openai-dotnet/blob/main/LICENSE
### Azure.AI.OpenAI
- **Source**: https://github.com/Azure/azure-sdk-for-net
- **Version**: 2.2.0-beta2
- **License**: MIT License
- **License Link**: https://github.com/Azure/azure-sdk-for-net/blob/main/LICENSE.txt
## Fonts
### JetBrainsMono
- **Source**: https://github.com/JetBrains/JetBrainsMono
- **Commit**: v2.304
- **License**: SIL Open Font License, Version 1.1
- **License Link**: https://github.com/JetBrains/JetBrainsMono/blob/v2.304/OFL.txt
## Grammar Files
### haxe-TmLanguage
- **Source**: https://github.com/vshaxe/haxe-TmLanguage
- **Commit**: ddad8b4c6d0781ac20be0481174ec1be772c5da5
- **License**: MIT License
- **License Link**: https://github.com/vshaxe/haxe-TmLanguage/blob/ddad8b4c6d0781ac20be0481174ec1be772c5da5/LICENSE.md
### coc-toml
- **Source**: https://github.com/kkiyama117/coc-toml
- **Commit**: aac3e0c65955c03314b2733041b19f903b7cc447
- **License**: MIT License
- **License Link**: https://github.com/kkiyama117/coc-toml/blob/aac3e0c65955c03314b2733041b19f903b7cc447/LICENSE
### eclipse-buildship
- **Source**: https://github.com/eclipse/buildship
- **Commit**: 6bb773e7692f913dec27105129ebe388de34e68b
- **License**: Eclipse Public License 1.0
- **License Link**: https://github.com/eclipse-buildship/buildship/blob/6bb773e7692f913dec27105129ebe388de34e68b/README.md
### vscode-jsp-lang
- **Source**: https://github.com/samuel-weinhardt/vscode-jsp-lang
- **Commit**: 0e89ecdb13650dbbe5a1e85b47b2e1530bf2f355
- **License**: MIT License
- **License Link**: https://github.com/samuel-weinhardt/vscode-jsp-lang/blob/0e89ecdb13650dbbe5a1e85b47b2e1530bf2f355/LICENSE

View file

@ -1,114 +1,228 @@
### de_DE.axaml: 99.21%
# Translation Status
This document shows the translation status of each locale file in the repository.
## Details
### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)
### ![de__DE](https://img.shields.io/badge/de__DE-%E2%88%9A-brightgreen)
### ![es__ES](https://img.shields.io/badge/es__ES-99.13%25-yellow)
<details>
<summary>Missing Keys</summary>
<summary>Missing keys in es_ES.axaml</summary>
- Text.BranchUpstreamInvalid
- Text.Configure.CustomAction.WaitForExit
- Text.Diff.First
- Text.Diff.Last
- Text.Preferences.AI.Streaming
- Text.StashCM.SaveAsPatch
- Text.CommitCM.PushRevision
- Text.Merge.Edit
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.WorkingCopy.AddToGitIgnore.InFolder
</details>
### es_ES.axaml: 99.87%
### ![fr__FR](https://img.shields.io/badge/fr__FR-91.19%25-yellow)
<details>
<summary>Missing Keys</summary>
<summary>Missing keys in fr_FR.axaml</summary>
- Text.StashCM.SaveAsPatch
- Text.Avatar.Load
- Text.Bisect
- Text.Bisect.Abort
- Text.Bisect.Bad
- Text.Bisect.Detecting
- Text.Bisect.Good
- Text.Bisect.Skip
- Text.Bisect.WaitingForRange
- Text.BranchCM.ResetToSelectedCommit
- Text.Checkout.RecurseSubmodules
- Text.Checkout.WithFastForward
- Text.Checkout.WithFastForward.Upstream
- Text.CommitCM.CopyAuthor
- Text.CommitCM.CopyCommitter
- Text.CommitCM.CopySubject
- Text.CommitCM.PushRevision
- Text.CommitDetail.Changes.Count
- Text.CommitMessageTextBox.SubjectCount
- Text.Configure.Git.PreferredMergeMode
- Text.ConfirmEmptyCommit.Continue
- Text.ConfirmEmptyCommit.NoLocalChanges
- Text.ConfirmEmptyCommit.StageAllThenCommit
- Text.ConfirmEmptyCommit.WithLocalChanges
- Text.CreateBranch.OverwriteExisting
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
- Text.Diff.Submodule.Deleted
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
- Text.Launcher.Workspaces
- Text.Launcher.Pages
- Text.Merge.Edit
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Pull.RecurseSubmodules
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
- Text.Repository.BranchSort.ByName
- Text.Repository.ClearStashes
- Text.Repository.Search.ByContent
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.ViewLogs
- Text.Repository.Visit
- Text.ResetWithoutCheckout
- Text.ResetWithoutCheckout.MoveTo
- Text.ResetWithoutCheckout.Target
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.Submodule.Deinit
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
- Text.Submodule.Status.RevisionChanged
- Text.Submodule.Status.Unmerged
- Text.Submodule.URL
- Text.ViewLogs
- Text.ViewLogs.Clear
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ConfirmCommitWithFilter
- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
- Text.WorkingCopy.Conflicts.UseMine
- Text.WorkingCopy.Conflicts.UseTheirs
- Text.WorkingCopy.ResetAuthor
</details>
### fr_FR.axaml: 91.80%
### ![it__IT](https://img.shields.io/badge/it__IT-96.53%25-yellow)
<details>
<summary>Missing Keys</summary>
<summary>Missing keys in it_IT.axaml</summary>
- Text.AIAssistant.Regen
- Text.AIAssistant.Use
- Text.ApplyStash
- Text.ApplyStash.DropAfterApply
- Text.ApplyStash.RestoreIndex
- Text.ApplyStash.Stash
- Text.BranchCM.CustomAction
- Text.BranchUpstreamInvalid
- Text.Clone.RecurseSubmodules
- Text.Configure.CustomAction.Scope.Branch
- Text.Configure.CustomAction.WaitForExit
- Text.CreateBranch.Name.WarnSpace
- Text.DeleteRepositoryNode.Path
- Text.DeleteRepositoryNode.TipForGroup
- Text.DeleteRepositoryNode.TipForRepository
- Text.Diff.First
- Text.Diff.Last
- Text.InProgress.CherryPick.Head
- Text.InProgress.Merge.Operating
- Text.InProgress.Rebase.StoppedAt
- Text.InProgress.Revert.Head
- Text.Merge.Source
- Text.MergeMultiple
- Text.MergeMultiple.CommitChanges
- Text.MergeMultiple.Strategy
- Text.MergeMultiple.Targets
- Text.Preferences.AI.Streaming
- Text.Preferences.Appearance.FontSize
- Text.Preferences.Appearance.FontSize.Default
- Text.Preferences.Appearance.FontSize.Editor
- Text.Preferences.General.DateFormat
- Text.Preferences.General.ShowChildren
- Text.Preferences.Git.SSLVerify
- Text.Repository.CustomActions
- Text.Avatar.Load
- Text.BranchCM.ResetToSelectedCommit
- Text.Checkout.WithFastForward
- Text.Checkout.WithFastForward.Upstream
- Text.CommitCM.PushRevision
- Text.CommitDetail.Changes.Count
- Text.CreateBranch.OverwriteExisting
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
- Text.Diff.Submodule.Deleted
- Text.Hotkeys.Global.SwitchWorkspace
- Text.Hotkeys.Global.SwitchTab
- Text.Launcher.Workspaces
- Text.Launcher.Pages
- Text.Merge.Edit
- Text.Pull.RecurseSubmodules
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.Repository.ClearStashes
- Text.ResetWithoutCheckout
- Text.ResetWithoutCheckout.MoveTo
- Text.ResetWithoutCheckout.Target
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.Submodule.Deinit
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ResetAuthor
</details>
### ![ja__JP](https://img.shields.io/badge/ja__JP-90.94%25-yellow)
<details>
<summary>Missing keys in ja_JP.axaml</summary>
- Text.Avatar.Load
- Text.Bisect
- Text.Bisect.Abort
- Text.Bisect.Bad
- Text.Bisect.Detecting
- Text.Bisect.Good
- Text.Bisect.Skip
- Text.Bisect.WaitingForRange
- Text.BranchCM.CompareWithCurrent
- Text.BranchCM.ResetToSelectedCommit
- Text.Checkout.RecurseSubmodules
- Text.Checkout.WithFastForward
- Text.Checkout.WithFastForward.Upstream
- Text.CommitCM.CopyAuthor
- Text.CommitCM.CopyCommitter
- Text.CommitCM.CopySubject
- Text.CommitCM.PushRevision
- Text.CommitDetail.Changes.Count
- Text.CommitMessageTextBox.SubjectCount
- Text.Configure.Git.PreferredMergeMode
- Text.ConfirmEmptyCommit.Continue
- Text.ConfirmEmptyCommit.NoLocalChanges
- Text.ConfirmEmptyCommit.StageAllThenCommit
- Text.ConfirmEmptyCommit.WithLocalChanges
- Text.CreateBranch.OverwriteExisting
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
- Text.Diff.Submodule.Deleted
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
- Text.Launcher.Workspaces
- Text.Launcher.Pages
- Text.Merge.Edit
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Pull.RecurseSubmodules
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
- Text.Repository.BranchSort.ByName
- Text.Repository.ClearStashes
- Text.Repository.FilterCommits
- Text.Repository.FilterCommits.Default
- Text.Repository.FilterCommits.Exclude
- Text.Repository.FilterCommits.Include
- Text.Repository.HistoriesLayout
- Text.Repository.HistoriesLayout.Horizontal
- Text.Repository.HistoriesLayout.Vertical
- Text.Repository.HistoriesOrder
- Text.Repository.HistoriesOrder.ByDate
- Text.Repository.HistoriesOrder.Topo
- Text.Repository.Notifications.Clear
- Text.Repository.Skip
- Text.Repository.Tags.OrderByCreatorDate
- Text.Repository.Tags.OrderByNameAsc
- Text.Repository.Tags.OrderByNameDes
- Text.Repository.Tags.Sort
- Text.Repository.UseRelativeTimeInHistories
- Text.ScanRepositories
- Text.SetUpstream
- Text.SetUpstream.Local
- Text.SetUpstream.Unset
- Text.SetUpstream.Upstream
- Text.SHALinkCM.NavigateTo
- Text.Stash.AutoRestore
- Text.Stash.AutoRestore.Tip
- Text.StashCM.SaveAsPatch
- Text.WorkingCopy.CommitToEdit
- Text.WorkingCopy.SignOff
- Text.Repository.Search.ByContent
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.ViewLogs
- Text.Repository.Visit
- Text.ResetWithoutCheckout
- Text.ResetWithoutCheckout.MoveTo
- Text.ResetWithoutCheckout.Target
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.Submodule.Deinit
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
- Text.Submodule.Status.RevisionChanged
- Text.Submodule.Status.Unmerged
- Text.Submodule.URL
- Text.ViewLogs
- Text.ViewLogs.Clear
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ConfirmCommitWithFilter
- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
- Text.WorkingCopy.Conflicts.UseMine
- Text.WorkingCopy.Conflicts.UseTheirs
- Text.WorkingCopy.ResetAuthor
</details>
### it_IT.axaml: 100.00%
### ![pt__BR](https://img.shields.io/badge/pt__BR-83.25%25-yellow)
<details>
<summary>Missing Keys</summary>
</details>
### pt_BR.axaml: 91.53%
<details>
<summary>Missing Keys</summary>
<summary>Missing keys in pt_BR.axaml</summary>
- Text.AIAssistant.Regen
- Text.AIAssistant.Use
@ -116,41 +230,88 @@
- Text.ApplyStash.DropAfterApply
- Text.ApplyStash.RestoreIndex
- Text.ApplyStash.Stash
- Text.Avatar.Load
- Text.Bisect
- Text.Bisect.Abort
- Text.Bisect.Bad
- Text.Bisect.Detecting
- Text.Bisect.Good
- Text.Bisect.Skip
- Text.Bisect.WaitingForRange
- Text.BranchCM.CustomAction
- Text.BranchCM.MergeMultiBranches
- Text.BranchCM.ResetToSelectedCommit
- Text.BranchUpstreamInvalid
- Text.Checkout.RecurseSubmodules
- Text.Checkout.WithFastForward
- Text.Checkout.WithFastForward.Upstream
- Text.Clone.RecurseSubmodules
- Text.CommitCM.CopyAuthor
- Text.CommitCM.CopyCommitter
- Text.CommitCM.CopySubject
- Text.CommitCM.Merge
- Text.CommitCM.MergeMultiple
- Text.CommitCM.PushRevision
- Text.CommitDetail.Changes.Count
- Text.CommitDetail.Files.Search
- Text.CommitDetail.Info.Children
- Text.CommitMessageTextBox.SubjectCount
- Text.Configure.CustomAction.Scope.Branch
- Text.Configure.CustomAction.WaitForExit
- Text.Configure.Git.PreferredMergeMode
- Text.Configure.IssueTracker.AddSampleGiteeIssue
- Text.Configure.IssueTracker.AddSampleGiteePullRequest
- Text.ConfirmEmptyCommit.Continue
- Text.ConfirmEmptyCommit.NoLocalChanges
- Text.ConfirmEmptyCommit.StageAllThenCommit
- Text.ConfirmEmptyCommit.WithLocalChanges
- Text.CopyFullPath
- Text.CreateBranch.Name.WarnSpace
- Text.CreateBranch.OverwriteExisting
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
- Text.DeleteRepositoryNode.Path
- Text.DeleteRepositoryNode.TipForGroup
- Text.DeleteRepositoryNode.TipForRepository
- Text.Diff.First
- Text.Diff.Last
- Text.Diff.Submodule.Deleted
- Text.Diff.UseBlockNavigation
- Text.Fetch.Force
- Text.FileCM.ResolveUsing
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.Clone
- Text.Hotkeys.Global.SwitchWorkspace
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
- Text.InProgress.CherryPick.Head
- Text.InProgress.Merge.Operating
- Text.InProgress.Rebase.StoppedAt
- Text.InProgress.Revert.Head
- Text.Launcher.Workspaces
- Text.Launcher.Pages
- Text.Merge.Edit
- Text.Merge.Source
- Text.MergeMultiple
- Text.MergeMultiple.CommitChanges
- Text.MergeMultiple.Strategy
- Text.MergeMultiple.Targets
- Text.Preferences.AI.Streaming
- Text.Preferences.Appearance.EditorTabWidth
- Text.Preferences.General.DateFormat
- Text.Preferences.General.ShowChildren
- Text.Preferences.General.ShowTagsInGraph
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Preferences.Git.SSLVerify
- Text.Pull.RecurseSubmodules
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
- Text.Repository.BranchSort.ByName
- Text.Repository.ClearStashes
- Text.Repository.FilterCommits
- Text.Repository.HistoriesLayout
- Text.Repository.HistoriesLayout.Horizontal
@ -158,57 +319,201 @@
- Text.Repository.HistoriesOrder
- Text.Repository.Notifications.Clear
- Text.Repository.OnlyHighlightCurrentBranchInHistories
- Text.Repository.Search.ByContent
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.Skip
- Text.Repository.Tags.OrderByCreatorDate
- Text.Repository.Tags.OrderByNameAsc
- Text.Repository.Tags.OrderByNameDes
- Text.Repository.Tags.OrderByName
- Text.Repository.Tags.Sort
- Text.Repository.UseRelativeTimeInHistories
- Text.Repository.ViewLogs
- Text.Repository.Visit
- Text.ResetWithoutCheckout
- Text.ResetWithoutCheckout.MoveTo
- Text.ResetWithoutCheckout.Target
- Text.SetUpstream
- Text.SetUpstream.Local
- Text.SetUpstream.Unset
- Text.SetUpstream.Upstream
- Text.SHALinkCM.NavigateTo
- Text.Stash.AutoRestore
- Text.Stash.AutoRestore.Tip
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.StashCM.SaveAsPatch
- Text.Submodule.Deinit
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
- Text.Submodule.Status.RevisionChanged
- Text.Submodule.Status.Unmerged
- Text.Submodule.URL
- Text.ViewLogs
- Text.ViewLogs.Clear
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.CommitToEdit
- Text.WorkingCopy.ConfirmCommitWithFilter
- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
- Text.WorkingCopy.Conflicts.UseMine
- Text.WorkingCopy.Conflicts.UseTheirs
- Text.WorkingCopy.ResetAuthor
- Text.WorkingCopy.SignOff
</details>
### ru_RU.axaml: 99.07%
### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen)
### ![ta__IN](https://img.shields.io/badge/ta__IN-91.07%25-yellow)
<details>
<summary>Missing Keys</summary>
<summary>Missing keys in ta_IN.axaml</summary>
- Text.BranchCM.CustomAction
- Text.BranchUpstreamInvalid
- Text.Configure.CustomAction.Scope.Branch
- Text.Diff.First
- Text.Diff.Last
- Text.Preferences.AI.Streaming
- Text.StashCM.SaveAsPatch
- Text.Avatar.Load
- Text.Bisect
- Text.Bisect.Abort
- Text.Bisect.Bad
- Text.Bisect.Detecting
- Text.Bisect.Good
- Text.Bisect.Skip
- Text.Bisect.WaitingForRange
- Text.BranchCM.CompareWithCurrent
- Text.BranchCM.ResetToSelectedCommit
- Text.Checkout.RecurseSubmodules
- Text.Checkout.WithFastForward
- Text.Checkout.WithFastForward.Upstream
- Text.CommitCM.CopyAuthor
- Text.CommitCM.CopyCommitter
- Text.CommitCM.CopySubject
- Text.CommitCM.PushRevision
- Text.CommitDetail.Changes.Count
- Text.CommitMessageTextBox.SubjectCount
- Text.Configure.Git.PreferredMergeMode
- Text.ConfirmEmptyCommit.Continue
- Text.ConfirmEmptyCommit.NoLocalChanges
- Text.ConfirmEmptyCommit.StageAllThenCommit
- Text.ConfirmEmptyCommit.WithLocalChanges
- Text.CreateBranch.OverwriteExisting
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
- Text.Diff.Submodule.Deleted
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
- Text.Launcher.Workspaces
- Text.Launcher.Pages
- Text.Merge.Edit
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Pull.RecurseSubmodules
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
- Text.Repository.BranchSort.ByName
- Text.Repository.ClearStashes
- Text.Repository.Search.ByContent
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.ViewLogs
- Text.Repository.Visit
- Text.ResetWithoutCheckout
- Text.ResetWithoutCheckout.MoveTo
- Text.ResetWithoutCheckout.Target
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.Submodule.Deinit
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
- Text.Submodule.Status.RevisionChanged
- Text.Submodule.Status.Unmerged
- Text.Submodule.URL
- Text.UpdateSubmodules.Target
- Text.ViewLogs
- Text.ViewLogs.Clear
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.Conflicts.OpenExternalMergeTool
- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts
- Text.WorkingCopy.Conflicts.UseMine
- Text.WorkingCopy.Conflicts.UseTheirs
- Text.WorkingCopy.ResetAuthor
</details>
### zh_CN.axaml: 100.00%
### ![uk__UA](https://img.shields.io/badge/uk__UA-92.31%25-yellow)
<details>
<summary>Missing Keys</summary>
<summary>Missing keys in uk_UA.axaml</summary>
- Text.Avatar.Load
- Text.Bisect
- Text.Bisect.Abort
- Text.Bisect.Bad
- Text.Bisect.Detecting
- Text.Bisect.Good
- Text.Bisect.Skip
- Text.Bisect.WaitingForRange
- Text.BranchCM.ResetToSelectedCommit
- Text.Checkout.RecurseSubmodules
- Text.Checkout.WithFastForward
- Text.Checkout.WithFastForward.Upstream
- Text.CommitCM.CopyAuthor
- Text.CommitCM.CopyCommitter
- Text.CommitCM.CopySubject
- Text.CommitCM.PushRevision
- Text.CommitDetail.Changes.Count
- Text.CommitMessageTextBox.SubjectCount
- Text.ConfigureWorkspace.Name
- Text.CreateBranch.OverwriteExisting
- Text.DeinitSubmodule
- Text.DeinitSubmodule.Force
- Text.DeinitSubmodule.Path
- Text.Diff.Submodule.Deleted
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
- Text.Launcher.Workspaces
- Text.Launcher.Pages
- Text.Merge.Edit
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Pull.RecurseSubmodules
- Text.Push.Revision
- Text.Push.Revision.Title
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
- Text.Repository.BranchSort.ByName
- Text.Repository.ClearStashes
- Text.Repository.Search.ByContent
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.ViewLogs
- Text.Repository.Visit
- Text.ResetWithoutCheckout
- Text.ResetWithoutCheckout.MoveTo
- Text.ResetWithoutCheckout.Target
- Text.Stash.Mode
- Text.StashCM.CopyMessage
- Text.Submodule.Deinit
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
- Text.Submodule.Status.RevisionChanged
- Text.Submodule.Status.Unmerged
- Text.Submodule.URL
- Text.ViewLogs
- Text.ViewLogs.Clear
- Text.ViewLogs.CopyLog
- Text.ViewLogs.Delete
- Text.WorkingCopy.AddToGitIgnore.InFolder
- Text.WorkingCopy.ResetAuthor
</details>
### zh_TW.axaml: 100.00%
### ![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)
<details>
<summary>Missing Keys</summary>
</details>
### ![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)

View file

@ -1 +1 @@
2025.07
2025.23

View file

@ -12,4 +12,4 @@
dotnet publish -c Release -r $RUNTIME_IDENTIFIER -o $DESTINATION_FOLDER src/SourceGit.csproj
```
> [!NOTE]
> Please replace the `$RUNTIME_IDENTIFIER` with one of `win-x64`,`win-arm64`,`linux-x64`,`linux-arm64`,`osx-x64`,`osx-arm64`, and replece the `$DESTINATION_FOLDER` with the real path that will store the output executable files.
> Please replace the `$RUNTIME_IDENTIFIER` with one of `win-x64`,`win-arm64`,`linux-x64`,`linux-arm64`,`osx-x64`,`osx-arm64`, and replace the `$DESTINATION_FOLDER` with the real path that will store the output executable files.

View file

@ -1,7 +1,8 @@
Package: sourcegit
Version: 8.23
Version: 2025.10
Priority: optional
Depends: libx11-6, libice6, libsm6, libicu | libicu76 | libicu74 | libicu72 | libicu71 | libicu70 | libicu69 | libicu68 | libicu67 | libicu66 | libicu65 | libicu63 | libicu60 | libicu57 | libicu55 | libicu52, xdg-utils
Architecture: amd64
Installed-Size: 60440
Maintainer: longshuang@msn.cn
Description: Open-source & Free Git GUI Client

View file

@ -0,0 +1,32 @@
#!/bin/sh
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see http://www.debian.org/doc/debian-policy/
case "$1" in
install|upgrade)
# Check if SourceGit is running and stop it
if pgrep -f '/opt/sourcegit/sourcegit' > /dev/null; then
echo "Stopping running SourceGit instance..."
pkill -f '/opt/sourcegit/sourcegit' || true
# Give the process a moment to terminate
sleep 1
fi
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View file

@ -0,0 +1,35 @@
#!/bin/sh
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|upgrade|deconfigure)
if pgrep -f '/opt/sourcegit/sourcegit' > /dev/null; then
echo "Stopping running SourceGit instance..."
pkill -f '/opt/sourcegit/sourcegit' || true
# Give the process a moment to terminate
sleep 1
fi
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View file

@ -6,7 +6,6 @@ const repoRoot = path.join(__dirname, '../../');
const localesDir = path.join(repoRoot, 'src/Resources/Locales');
const enUSFile = path.join(localesDir, 'en_US.axaml');
const outputFile = path.join(repoRoot, 'TRANSLATION.md');
const readmeFile = path.join(repoRoot, 'README.md');
const parser = new xml2js.Parser();
@ -15,49 +14,70 @@ async function parseXml(filePath) {
return parser.parseStringPromise(data);
}
async function filterAndSortTranslations(localeData, enUSKeys, enUSData) {
const strings = localeData.ResourceDictionary['x:String'];
// Remove keys that don't exist in English file
const filtered = strings.filter(item => enUSKeys.has(item.$['x:Key']));
// Sort based on the key order in English file
const enUSKeysArray = enUSData.ResourceDictionary['x:String'].map(item => item.$['x:Key']);
filtered.sort((a, b) => {
const aIndex = enUSKeysArray.indexOf(a.$['x:Key']);
const bIndex = enUSKeysArray.indexOf(b.$['x:Key']);
return aIndex - bIndex;
});
return filtered;
}
async function calculateTranslationRate() {
const enUSData = await parseXml(enUSFile);
const enUSKeys = new Set(enUSData.ResourceDictionary['x:String'].map(item => item.$['x:Key']));
const translationRates = [];
const badges = [];
const files = (await fs.readdir(localesDir)).filter(file => file !== 'en_US.axaml' && file.endsWith('.axaml'));
// Add en_US badge first
badges.push(`[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md)`);
const lines = [];
lines.push('# Translation Status');
lines.push('This document shows the translation status of each locale file in the repository.');
lines.push(`## Details`);
lines.push(`### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)`);
for (const file of files) {
const locale = file.replace('.axaml', '').replace('_', '__');
const filePath = path.join(localesDir, file);
const localeData = await parseXml(filePath);
const localeKeys = new Set(localeData.ResourceDictionary['x:String'].map(item => item.$['x:Key']));
const missingKeys = [...enUSKeys].filter(key => !localeKeys.has(key));
const translationRate = ((enUSKeys.size - missingKeys.length) / enUSKeys.size) * 100;
translationRates.push(`### ${file}: ${translationRate.toFixed(2)}%\n`);
translationRates.push(`<details>\n<summary>Missing Keys</summary>\n\n${missingKeys.map(key => `- ${key}`).join('\n')}\n\n</details>`);
// Sort and clean up extra translations
const sortedAndCleaned = await filterAndSortTranslations(localeData, enUSKeys, enUSData);
localeData.ResourceDictionary['x:String'] = sortedAndCleaned;
// Add badges
const locale = file.replace('.axaml', '').replace('_', '__');
if (translationRate === 100) {
badges.push(`[![${locale}](https://img.shields.io/badge/${locale}-%E2%88%9A-brightgreen)](TRANSLATION.md)`);
// Save the updated file
const builder = new xml2js.Builder({
headless: true,
renderOpts: { pretty: true, indent: ' ' }
});
let xmlStr = builder.buildObject(localeData);
// Add an empty line before the first x:String
xmlStr = xmlStr.replace(' <x:String', '\n <x:String');
await fs.writeFile(filePath, xmlStr + '\n', 'utf8');
if (missingKeys.length > 0) {
const progress = ((enUSKeys.size - missingKeys.length) / enUSKeys.size) * 100;
const badgeColor = progress >= 75 ? 'yellow' : 'red';
lines.push(`### ![${locale}](https://img.shields.io/badge/${locale}-${progress.toFixed(2)}%25-${badgeColor})`);
lines.push(`<details>\n<summary>Missing keys in ${file}</summary>\n\n${missingKeys.map(key => `- ${key}`).join('\n')}\n\n</details>`)
} else {
const badgeColor = translationRate >= 75 ? 'yellow' : 'red';
badges.push(`[![${locale}](https://img.shields.io/badge/${locale}-${translationRate.toFixed(2)}%25-${badgeColor})](TRANSLATION.md)`);
}
lines.push(`### ![${locale}](https://img.shields.io/badge/${locale}-%E2%88%9A-brightgreen)`);
}
}
console.log(translationRates.join('\n\n'));
await fs.writeFile(outputFile, translationRates.join('\n\n') + '\n', 'utf8');
// Update README.md
let readmeContent = await fs.readFile(readmeFile, 'utf8');
const badgeSection = `## Translation Status\n\n${badges.join(' ')}`;
console.log(badgeSection);
readmeContent = readmeContent.replace(/## Translation Status\n\n.*\n\n/, badgeSection + '\n\n');
await fs.writeFile(readmeFile, readmeContent, 'utf8');
const content = lines.join('\n\n');
console.log(content);
await fs.writeFile(outputFile, content, 'utf8');
}
calculateTranslationRate().catch(err => console.error(err));

View file

@ -56,8 +56,15 @@ cp -f SourceGit/* resources/deb/opt/sourcegit
ln -rsf resources/deb/opt/sourcegit/sourcegit resources/deb/usr/bin
cp -r resources/_common/applications resources/deb/usr/share
cp -r resources/_common/icons resources/deb/usr/share
sed -i -e "s/^Version:.*/Version: $VERSION/" -e "s/^Architecture:.*/Architecture: $arch/" resources/deb/DEBIAN/control
dpkg-deb --root-owner-group --build resources/deb "sourcegit_$VERSION-1_$arch.deb"
# Calculate installed size in KB
installed_size=$(du -sk resources/deb | cut -f1)
# Update the control file
sed -i -e "s/^Version:.*/Version: $VERSION/" \
-e "s/^Architecture:.*/Architecture: $arch/" \
-e "s/^Installed-Size:.*/Installed-Size: $installed_size/" \
resources/deb/DEBIAN/control
# Build deb package with gzip compression
dpkg-deb -Zgzip --root-owner-group --build resources/deb "sourcegit_$VERSION-1_$arch.deb"
rpmbuild -bb --target="$target" resources/rpm/SPECS/build.spec --define "_topdir $(pwd)/resources/rpm" --define "_version $VERSION"
mv "resources/rpm/RPMS/$target/sourcegit-$VERSION-1.$target.rpm" ./

View file

@ -9,4 +9,8 @@ cd build
rm -rf SourceGit/*.pdb
zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
powershell -Command "Compress-Archive -Path SourceGit -DestinationPath \"sourcegit_$VERSION.$RUNTIME.zip\" -Force"
else
zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit
fi

View file

@ -37,10 +37,10 @@ namespace SourceGit
}
}
public static readonly Command OpenPreferencesCommand = new Command(_ => OpenDialog(new Views.Preferences()));
public static readonly Command OpenHotkeysCommand = new Command(_ => OpenDialog(new Views.Hotkeys()));
public static readonly Command OpenPreferencesCommand = new Command(_ => ShowWindow(new Views.Preferences(), false));
public static readonly Command OpenHotkeysCommand = new Command(_ => ShowWindow(new Views.Hotkeys(), false));
public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir));
public static readonly Command OpenAboutCommand = new Command(_ => OpenDialog(new Views.About()));
public static readonly Command OpenAboutCommand = new Command(_ => ShowWindow(new Views.About(), false));
public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true));
public static readonly Command QuitCommand = new Command(_ => Quit(0));
public static readonly Command CopyTextBlockCommand = new Command(p =>

View file

@ -16,10 +16,13 @@
<ResourceInclude x:Key="fr_FR" Source="/Resources/Locales/fr_FR.axaml"/>
<ResourceInclude x:Key="it_IT" Source="/Resources/Locales/it_IT.axaml"/>
<ResourceInclude x:Key="pt_BR" Source="/Resources/Locales/pt_BR.axaml"/>
<ResourceInclude x:Key="uk_UA" Source="/Resources/Locales/uk_UA.axaml"/>
<ResourceInclude x:Key="ru_RU" Source="/Resources/Locales/ru_RU.axaml"/>
<ResourceInclude x:Key="zh_CN" Source="/Resources/Locales/zh_CN.axaml"/>
<ResourceInclude x:Key="zh_TW" Source="/Resources/Locales/zh_TW.axaml"/>
<ResourceInclude x:Key="es_ES" Source="/Resources/Locales/es_ES.axaml"/>
<ResourceInclude x:Key="ja_JP" Source="/Resources/Locales/ja_JP.axaml"/>
<ResourceInclude x:Key="ta_IN" Source="/Resources/Locales/ta_IN.axaml"/>
</ResourceDictionary>
</Application.Resources>
@ -32,7 +35,7 @@
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="{DynamicResource Text.About.Menu}" Command="{x:Static s:App.OpenAboutCommand}"/>
<NativeMenuItem Header="{DynamicResource Text.Hotkeys}" Command="{x:Static s:App.OpenHotkeysCommand}"/>
<NativeMenuItem Header="{DynamicResource Text.Hotkeys}" Command="{x:Static s:App.OpenHotkeysCommand}" Gesture="F1"/>
<NativeMenuItem Header="{DynamicResource Text.SelfUpdate}" Command="{x:Static s:App.CheckForUpdateCommand}" IsVisible="{x:Static s:App.IsCheckForUpdateCommandVisible}"/>
<NativeMenuItemSeparator/>
<NativeMenuItem Header="{DynamicResource Text.Preferences}" Command="{x:Static s:App.OpenPreferencesCommand}" Gesture="⌘+,"/>

View file

@ -6,6 +6,7 @@ using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@ -37,7 +38,6 @@ namespace SourceGit
TaskScheduler.UnobservedTaskException += (_, e) =>
{
LogException(e.Exception);
e.SetObserved();
};
@ -77,13 +77,72 @@ namespace SourceGit
Native.OS.SetupApp(builder);
return builder;
}
public static void LogException(Exception ex)
{
if (ex == null)
return;
var builder = new StringBuilder();
builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n");
builder.Append("----------------------------\n");
builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n");
builder.Append($"OS: {Environment.OSVersion}\n");
builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n");
builder.Append($"Source: {ex.Source}\n");
builder.Append($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}\n");
builder.Append($"User: {Environment.UserName}\n");
builder.Append($"App Start Time: {Process.GetCurrentProcess().StartTime}\n");
builder.Append($"Exception Time: {DateTime.Now}\n");
builder.Append($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB\n");
builder.Append($"---------------------------\n\n");
builder.Append(ex);
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log");
File.WriteAllText(file, builder.ToString());
}
#endregion
#region Utility Functions
public static void OpenDialog(Window window)
public static void ShowWindow(object data, bool showAsDialog)
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
window.ShowDialog(owner);
var impl = (Views.ChromelessWindow target, bool isDialog) =>
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
if (isDialog)
target.ShowDialog(owner);
else
target.Show(owner);
}
else
{
target.Show();
}
};
if (data is Views.ChromelessWindow window)
{
impl(window, showAsDialog);
return;
}
var dataTypeName = data.GetType().FullName;
if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal))
return;
var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views.");
var viewType = Type.GetType(viewTypeName);
if (viewType == null || !viewType.IsSubclassOf(typeof(Views.ChromelessWindow)))
return;
window = Activator.CreateInstance(viewType) as Views.ChromelessWindow;
if (window != null)
{
window.DataContext = data;
impl(window, showAsDialog);
}
}
public static void RaiseException(string context, string message)
@ -181,6 +240,9 @@ namespace SourceGit
app._fontsOverrides = null;
}
defaultFont = app.FixFontFamilyName(defaultFont);
monospaceFont = app.FixFontFamilyName(monospaceFont);
var resDic = new ResourceDictionary();
if (!string.IsNullOrEmpty(defaultFont))
resDic.Add("Fonts.Default", new FontFamily(defaultFont));
@ -239,7 +301,7 @@ namespace SourceGit
return await clipboard.GetTextAsync();
}
}
return default;
return null;
}
public static string Text(string key, params object[] args)
@ -261,8 +323,7 @@ namespace SourceGit
icon.Height = 12;
icon.Stretch = Stretch.Uniform;
var geo = Current?.FindResource(key) as StreamGeometry;
if (geo != null)
if (Current?.FindResource(key) is StreamGeometry geo)
icon.Data = geo;
return icon;
@ -276,7 +337,7 @@ namespace SourceGit
return null;
}
public static ViewModels.Launcher GetLauncer()
public static ViewModels.Launcher GetLauncher()
{
return Current is App app ? app._launcher : null;
}
@ -314,42 +375,46 @@ namespace SourceGit
{
BindingPlugins.DataValidators.RemoveAt(0);
// Disable tooltip if window is not active.
ToolTip.ToolTipOpeningEvent.AddClassHandler<Control>((c, e) =>
{
var topLevel = TopLevel.GetTopLevel(c);
if (topLevel is not Window { IsActive: true })
e.Cancel = true;
});
if (TryLaunchAsCoreEditor(desktop))
return;
if (TryLaunchAsAskpass(desktop))
return;
TryLaunchAsNormal(desktop);
_ipcChannel = new Models.IpcChannel();
if (!_ipcChannel.IsFirstInstance)
{
var arg = desktop.Args is { Length: > 0 } ? desktop.Args[0].Trim() : string.Empty;
if (!string.IsNullOrEmpty(arg))
{
if (arg.StartsWith('"') && arg.EndsWith('"'))
arg = arg.Substring(1, arg.Length - 2).Trim();
if (arg.Length > 0 && !Path.IsPathFullyQualified(arg))
arg = Path.GetFullPath(arg);
}
_ipcChannel.SendToFirstInstance(arg);
Environment.Exit(0);
}
else
{
_ipcChannel.MessageReceived += TryOpenRepository;
desktop.Exit += (_, _) => _ipcChannel.Dispose();
TryLaunchAsNormal(desktop);
}
}
}
#endregion
private static void LogException(Exception ex)
{
if (ex == null)
return;
var builder = new StringBuilder();
builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n");
builder.Append("----------------------------\n");
builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n");
builder.Append($"OS: {Environment.OSVersion}\n");
builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n");
builder.Append($"Source: {ex.Source}\n");
builder.Append($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}\n");
builder.Append($"User: {Environment.UserName}\n");
builder.Append($"App Start Time: {Process.GetCurrentProcess().StartTime}\n");
builder.Append($"Exception Time: {DateTime.Now}\n");
builder.Append($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB\n");
builder.Append($"---------------------------\n\n");
builder.Append(ex);
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log");
File.WriteAllText(file, builder.ToString());
}
private static bool TryLaunchAsRebaseTodoEditor(string[] args, out int exitCode)
{
exitCode = -1;
@ -418,21 +483,37 @@ namespace SourceGit
return true;
var gitDir = Path.GetDirectoryName(file)!;
var jobsFile = Path.Combine(gitDir, "sourcegit_rebase_jobs.json");
if (!File.Exists(jobsFile))
return true;
var collection = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.InteractiveRebaseJobCollection);
var origHeadFile = Path.Combine(gitDir, "rebase-merge", "orig-head");
var ontoFile = Path.Combine(gitDir, "rebase-merge", "onto");
var doneFile = Path.Combine(gitDir, "rebase-merge", "done");
if (!File.Exists(doneFile))
var jobsFile = Path.Combine(gitDir, "sourcegit_rebase_jobs.json");
if (!File.Exists(ontoFile) || !File.Exists(origHeadFile) || !File.Exists(doneFile) || !File.Exists(jobsFile))
return true;
var done = File.ReadAllText(doneFile).Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
if (done.Length > collection.Jobs.Count)
var origHead = File.ReadAllText(origHeadFile).Trim();
var onto = File.ReadAllText(ontoFile).Trim();
var collection = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.InteractiveRebaseJobCollection);
if (!collection.Onto.Equals(onto) || !collection.OrigHead.Equals(origHead))
return true;
var job = collection.Jobs[done.Length - 1];
File.WriteAllText(file, job.Message);
var done = File.ReadAllText(doneFile).Trim().Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
if (done.Length == 0)
return true;
var current = done[^1].Trim();
var match = REG_REBASE_TODO().Match(current);
if (!match.Success)
return true;
var sha = match.Groups[1].Value;
foreach (var job in collection.Jobs)
{
if (job.SHA.StartsWith(sha))
{
File.WriteAllText(file, job.Message);
break;
}
}
return true;
}
@ -440,15 +521,19 @@ namespace SourceGit
private bool TryLaunchAsCoreEditor(IClassicDesktopStyleApplicationLifetime desktop)
{
var args = desktop.Args;
if (args == null || args.Length <= 1 || !args[0].Equals("--core-editor", StringComparison.Ordinal))
if (args is not { Length: > 1 } || !args[0].Equals("--core-editor", StringComparison.Ordinal))
return false;
var file = args[1];
if (!File.Exists(file))
{
desktop.Shutdown(-1);
else
desktop.MainWindow = new Views.StandaloneCommitMessageEditor(file);
return true;
}
var editor = new Views.CommitMessageEditor();
editor.AsStandalone(file);
desktop.MainWindow = editor;
return true;
}
@ -461,7 +546,9 @@ namespace SourceGit
var args = desktop.Args;
if (args?.Length > 0)
{
desktop.MainWindow = new Views.Askpass(args[0]);
var askpass = new Views.Askpass();
askpass.TxtDescription.Text = args[0];
desktop.MainWindow = askpass;
return true;
}
@ -470,23 +557,54 @@ namespace SourceGit
private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
{
Native.OS.SetupEnternalTools();
Native.OS.SetupExternalTools();
Models.AvatarManager.Instance.Start();
string startupRepo = null;
if (desktop.Args != null && desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0]))
startupRepo = desktop.Args[0];
var pref = ViewModels.Preferences.Instance;
pref.SetCanModify();
_launcher = new ViewModels.Launcher(startupRepo);
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
#if !DISABLE_UPDATE_DETECTION
var pref = ViewModels.Preferences.Instance;
if (pref.ShouldCheck4UpdateOnStartup())
Check4Update();
#endif
}
private void TryOpenRepository(string repo)
{
if (!string.IsNullOrEmpty(repo) && Directory.Exists(repo))
{
var test = new Commands.QueryRepositoryRootPath(repo).ReadToEnd();
if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut))
{
Dispatcher.UIThread.Invoke(() =>
{
var node = ViewModels.Preferences.Instance.FindOrAddNodeByRepositoryPath(test.StdOut.Trim(), null, false);
ViewModels.Welcome.Instance.Refresh();
_launcher?.OpenRepositoryInTab(node, null);
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher wnd })
wnd.BringToTop();
});
return;
}
}
Dispatcher.UIThread.Invoke(() =>
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher launcher })
launcher.BringToTop();
});
}
private void Check4Update(bool manually = false)
{
Task.Run(async () =>
@ -523,7 +641,7 @@ namespace SourceGit
catch (Exception e)
{
if (manually)
ShowSelfUpdateResult(e);
ShowSelfUpdateResult(new Models.SelfUpdateFailed(e));
}
});
}
@ -532,14 +650,54 @@ namespace SourceGit
{
Dispatcher.UIThread.Post(() =>
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
var dialog = new Views.SelfUpdate() { DataContext = new ViewModels.SelfUpdate() { Data = data } };
dialog.ShowDialog(owner);
}
ShowWindow(new ViewModels.SelfUpdate() { Data = data }, true);
});
}
private string FixFontFamilyName(string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
var parts = input.Split(',');
var trimmed = new List<string>();
foreach (var part in parts)
{
var t = part.Trim();
if (string.IsNullOrEmpty(t))
continue;
// Collapse multiple spaces into single space
var prevChar = '\0';
var sb = new StringBuilder();
foreach (var c in t)
{
if (c == ' ' && prevChar == ' ')
continue;
sb.Append(c);
prevChar = c;
}
var name = sb.ToString();
if (name.Contains('#', StringComparison.Ordinal))
{
if (!name.Equals("fonts:Inter#Inter", StringComparison.Ordinal) &&
!name.Equals("fonts:SourceGit#JetBrains Mono", StringComparison.Ordinal))
continue;
}
trimmed.Add(name);
}
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
}
[GeneratedRegex(@"^[a-z]+\s+([a-fA-F0-9]{4,40})(\s+.*)?$")]
private static partial Regex REG_REBASE_TODO();
private Models.IpcChannel _ipcChannel = null;
private ViewModels.Launcher _launcher = null;
private ResourceDictionary _activeLocale = null;
private ResourceDictionary _themeOverrides = null;

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="SourceGit.Desktop"/>

View file

@ -1,7 +1,4 @@
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Add : Command
{
@ -12,20 +9,11 @@ namespace SourceGit.Commands
Args = includeUntracked ? "add ." : "add -u .";
}
public Add(string repo, List<string> changes)
public Add(string repo, Models.Change change)
{
WorkingDirectory = repo;
Context = repo;
var builder = new StringBuilder();
builder.Append("add --");
foreach (var c in changes)
{
builder.Append(" \"");
builder.Append(c);
builder.Append("\"");
}
Args = builder.ToString();
Args = $"add -- \"{change.Path}\"";
}
public Add(string repo, string pathspecFromFile)

View file

@ -1,23 +1,12 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Archive : Command
{
public Archive(string repo, string revision, string saveTo, Action<string> outputHandler)
public Archive(string repo, string revision, string saveTo)
{
WorkingDirectory = repo;
Context = repo;
Args = $"archive --format=zip --verbose --output=\"{saveTo}\" {revision}";
TraitErrorAsOutput = true;
_outputHandler = outputHandler;
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -1,75 +1,14 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public partial class AssumeUnchanged
public class AssumeUnchanged : Command
{
[GeneratedRegex(@"^(\w)\s+(.+)$")]
private static partial Regex REG_PARSE();
class ViewCommand : Command
public AssumeUnchanged(string repo, string file, bool bAdd)
{
public ViewCommand(string repo)
{
WorkingDirectory = repo;
Args = "ls-files -v";
RaiseError = false;
}
var mode = bAdd ? "--assume-unchanged" : "--no-assume-unchanged";
public List<string> Result()
{
Exec();
return _outs;
}
protected override void OnReadline(string line)
{
var match = REG_PARSE().Match(line);
if (!match.Success)
return;
if (match.Groups[1].Value == "h")
{
_outs.Add(match.Groups[2].Value);
}
}
private readonly List<string> _outs = new List<string>();
WorkingDirectory = repo;
Context = repo;
Args = $"update-index {mode} -- \"{file}\"";
}
class ModCommand : Command
{
public ModCommand(string repo, string file, bool bAdd)
{
var mode = bAdd ? "--assume-unchanged" : "--no-assume-unchanged";
WorkingDirectory = repo;
Context = repo;
Args = $"update-index {mode} -- \"{file}\"";
}
}
public AssumeUnchanged(string repo)
{
_repo = repo;
}
public List<string> View()
{
return new ViewCommand(_repo).Result();
}
public void Add(string file)
{
new ModCommand(_repo, file, true).Exec();
}
public void Remove(string file)
{
new ModCommand(_repo, file, false).Exec();
}
private readonly string _repo;
}
}

13
src/Commands/Bisect.cs Normal file
View file

@ -0,0 +1,13 @@
namespace SourceGit.Commands
{
public class Bisect : Command
{
public Bisect(string repo, string subcmd)
{
WorkingDirectory = repo;
Context = repo;
RaiseError = false;
Args = $"bisect {subcmd}";
}
}
}

View file

@ -21,10 +21,17 @@ namespace SourceGit.Commands
public Models.BlameData Result()
{
var succ = Exec();
if (!succ)
var rs = ReadToEnd();
if (!rs.IsSuccess)
return _result;
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
return new Models.BlameData();
ParseLine(line);
if (_result.IsBinary)
break;
}
if (_needUnifyCommitSHA)
@ -42,14 +49,9 @@ namespace SourceGit.Commands
return _result;
}
protected override void OnReadline(string line)
private void ParseLine(string line)
{
if (_result.IsBinary)
return;
if (string.IsNullOrEmpty(line))
return;
if (line.IndexOf('\0', StringComparison.Ordinal) >= 0)
if (line.Contains('\0', StringComparison.Ordinal))
{
_result.IsBinary = true;
_result.LineInfos.Clear();
@ -87,7 +89,7 @@ namespace SourceGit.Commands
private readonly Models.BlameData _result = new Models.BlameData();
private readonly StringBuilder _content = new StringBuilder();
private readonly string _dateFormat = Models.DateTimeFormat.Actived.DateOnly;
private readonly string _dateFormat = Models.DateTimeFormat.Active.DateOnly;
private string _lastSHA = string.Empty;
private bool _needUnifyCommitSHA = false;
private int _minSHALen = 64;

View file

@ -1,4 +1,6 @@
namespace SourceGit.Commands
using System.Text;
namespace SourceGit.Commands
{
public static class Branch
{
@ -11,29 +13,40 @@
return cmd.ReadToEnd().StdOut.Trim();
}
public static bool Create(string repo, string name, string basedOn)
public static bool Create(string repo, string name, string basedOn, bool force, Models.ICommandLog log)
{
var builder = new StringBuilder();
builder.Append("branch ");
if (force)
builder.Append("-f ");
builder.Append(name);
builder.Append(" ");
builder.Append(basedOn);
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch {name} {basedOn}";
cmd.Args = builder.ToString();
cmd.Log = log;
return cmd.Exec();
}
public static bool Rename(string repo, string name, string to)
public static bool Rename(string repo, string name, string to, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -M {name} {to}";
cmd.Log = log;
return cmd.Exec();
}
public static bool SetUpstream(string repo, string name, string upstream)
public static bool SetUpstream(string repo, string name, string upstream, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Log = log;
if (string.IsNullOrEmpty(upstream))
cmd.Args = $"branch {name} --unset-upstream";
@ -43,32 +56,27 @@
return cmd.Exec();
}
public static bool DeleteLocal(string repo, string name)
public static bool DeleteLocal(string repo, string name, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -D {name}";
cmd.Log = log;
return cmd.Exec();
}
public static bool DeleteRemote(string repo, string remote, string name)
public static bool DeleteRemote(string repo, string remote, string name, Models.ICommandLog log)
{
bool exists = new Remote(repo).HasBranch(remote, name);
if (exists)
return new Push(repo, remote, $"refs/heads/{name}", true) { Log = log }.Exec();
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
bool exists = new Remote(repo).HasBranch(remote, name);
if (exists)
{
cmd.SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
cmd.Args = $"push {remote} --delete {name}";
}
else
{
cmd.Args = $"branch -D -r {remote}/{name}";
}
cmd.Args = $"branch -D -r {remote}/{name}";
cmd.Log = log;
return cmd.Exec();
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
@ -12,19 +11,37 @@ namespace SourceGit.Commands
Context = repo;
}
public bool Branch(string branch, Action<string> onProgress)
public bool Branch(string branch, bool force)
{
Args = $"checkout --recurse-submodules --progress {branch}";
TraitErrorAsOutput = true;
_outputHandler = onProgress;
var builder = new StringBuilder();
builder.Append("checkout --progress ");
if (force)
builder.Append("--force ");
builder.Append(branch);
Args = builder.ToString();
return Exec();
}
public bool Branch(string branch, string basedOn, Action<string> onProgress)
public bool Branch(string branch, string basedOn, bool force, bool allowOverwrite)
{
Args = $"checkout --recurse-submodules --progress -b {branch} {basedOn}";
TraitErrorAsOutput = true;
_outputHandler = onProgress;
var builder = new StringBuilder();
builder.Append("checkout --progress ");
if (force)
builder.Append("--force ");
builder.Append(allowOverwrite ? "-B " : "-b ");
builder.Append(branch);
builder.Append(" ");
builder.Append(basedOn);
Args = builder.ToString();
return Exec();
}
public bool Commit(string commitId, bool force)
{
var option = force ? "--force" : string.Empty;
Args = $"checkout {option} --detach --progress {commitId}";
return Exec();
}
@ -61,20 +78,5 @@ namespace SourceGit.Commands
Args = $"checkout --no-overlay {revision} -- \"{file}\"";
return Exec();
}
public bool Commit(string commitId, Action<string> onProgress)
{
Args = $"checkout --detach --progress {commitId}";
TraitErrorAsOutput = true;
_outputHandler = onProgress;
return Exec();
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private Action<string> _outputHandler;
}
}

View file

@ -1,31 +1,12 @@
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Clean : Command
{
public Clean(string repo, bool includeIgnored)
public Clean(string repo)
{
WorkingDirectory = repo;
Context = repo;
Args = includeIgnored ? "clean -qfdx" : "clean -qfd";
}
public Clean(string repo, List<string> files)
{
var builder = new StringBuilder();
builder.Append("clean -qfd --");
foreach (var f in files)
{
builder.Append(" \"");
builder.Append(f);
builder.Append("\"");
}
WorkingDirectory = repo;
Context = repo;
Args = builder.ToString();
Args = "clean -qfdx";
}
}
}

View file

@ -1,16 +1,11 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Clone : Command
{
private readonly Action<string> _notifyProgress;
public Clone(string ctx, string path, string url, string localName, string sshKey, string extraArgs, Action<string> ouputHandler)
public Clone(string ctx, string path, string url, string localName, string sshKey, string extraArgs)
{
Context = ctx;
WorkingDirectory = path;
TraitErrorAsOutput = true;
SSHKey = sshKey;
Args = "clone --progress --verbose ";
@ -21,13 +16,6 @@ namespace SourceGit.Commands
if (!string.IsNullOrEmpty(localName))
Args += localName;
_notifyProgress = ouputHandler;
}
protected override void OnReadline(string line)
{
_notifyProgress?.Invoke(line);
}
}
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Avalonia.Threading;
@ -10,11 +11,6 @@ namespace SourceGit.Commands
{
public partial class Command
{
public class CancelToken
{
public bool Requested { get; set; } = false;
}
public class ReadToEndResult
{
public bool IsSuccess { get; set; } = false;
@ -30,82 +26,51 @@ namespace SourceGit.Commands
}
public string Context { get; set; } = string.Empty;
public CancelToken Cancel { get; set; } = null;
public CancellationToken CancellationToken { get; set; } = CancellationToken.None;
public string WorkingDirectory { get; set; } = null;
public EditorType Editor { get; set; } = EditorType.CoreEditor; // Only used in Exec() mode
public string SSHKey { get; set; } = string.Empty;
public string Args { get; set; } = string.Empty;
public bool RaiseError { get; set; } = true;
public bool TraitErrorAsOutput { get; set; } = false;
public Models.ICommandLog Log { get; set; } = null;
public bool Exec()
{
Log?.AppendLine($"$ git {Args}\n");
var start = CreateGitStartInfo();
var errs = new List<string>();
var proc = new Process() { StartInfo = start };
var isCancelled = false;
proc.OutputDataReceived += (_, e) =>
{
if (Cancel != null && Cancel.Requested)
{
isCancelled = true;
proc.CancelErrorRead();
proc.CancelOutputRead();
if (!proc.HasExited)
proc.Kill(true);
return;
}
if (e.Data != null)
OnReadline(e.Data);
};
proc.ErrorDataReceived += (_, e) =>
{
if (Cancel != null && Cancel.Requested)
{
isCancelled = true;
proc.CancelErrorRead();
proc.CancelOutputRead();
if (!proc.HasExited)
proc.Kill(true);
return;
}
if (string.IsNullOrEmpty(e.Data))
{
errs.Add(string.Empty);
return;
}
if (TraitErrorAsOutput)
OnReadline(e.Data);
// Ignore progress messages
if (e.Data.StartsWith("remote: Enumerating objects:", StringComparison.Ordinal))
return;
if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal))
return;
if (e.Data.StartsWith("remote: Compressing objects:", StringComparison.Ordinal))
return;
if (e.Data.StartsWith("Filtering content:", StringComparison.Ordinal))
return;
if (REG_PROGRESS().IsMatch(e.Data))
return;
errs.Add(e.Data);
};
proc.OutputDataReceived += (_, e) => HandleOutput(e.Data, errs);
proc.ErrorDataReceived += (_, e) => HandleOutput(e.Data, errs);
var dummy = null as Process;
var dummyProcLock = new object();
try
{
proc.Start();
// It not safe, please only use `CancellationToken` in readonly commands.
if (CancellationToken.CanBeCanceled)
{
dummy = proc;
CancellationToken.Register(() =>
{
lock (dummyProcLock)
{
if (dummy is { HasExited: false })
dummy.Kill();
}
});
}
}
catch (Exception e)
{
if (RaiseError)
Dispatcher.UIThread.Post(() => App.RaiseException(Context, e.Message));
Log?.AppendLine(string.Empty);
return false;
}
@ -113,10 +78,19 @@ namespace SourceGit.Commands
proc.BeginErrorReadLine();
proc.WaitForExit();
if (dummy != null)
{
lock (dummyProcLock)
{
dummy = null;
}
}
int exitCode = proc.ExitCode;
proc.Close();
Log?.AppendLine(string.Empty);
if (!isCancelled && exitCode != 0)
if (!CancellationToken.IsCancellationRequested && exitCode != 0)
{
if (RaiseError)
{
@ -163,11 +137,6 @@ namespace SourceGit.Commands
return rs;
}
protected virtual void OnReadline(string line)
{
// Implemented by derived class
}
private ProcessStartInfo CreateGitStartInfo()
{
var start = new ProcessStartInfo();
@ -192,13 +161,12 @@ namespace SourceGit.Commands
if (!start.Environment.ContainsKey("GIT_SSH_COMMAND") && !string.IsNullOrEmpty(SSHKey))
start.Environment.Add("GIT_SSH_COMMAND", $"ssh -i '{SSHKey}'");
// Force using en_US.UTF-8 locale to avoid GCM crash
// Force using en_US.UTF-8 locale
if (OperatingSystem.IsLinux())
start.Environment.Add("LANG", "en_US.UTF-8");
// Fix macOS `PATH` env
if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv))
start.Environment.Add("PATH", Native.OS.CustomPathEnv);
{
start.Environment.Add("LANG", "C");
start.Environment.Add("LC_ALL", "C");
}
// Force using this app as git editor.
switch (Editor)
@ -224,6 +192,28 @@ namespace SourceGit.Commands
return start;
}
private void HandleOutput(string line, List<string> errs)
{
line ??= string.Empty;
Log?.AppendLine(line);
// Lines to hide in error message.
if (line.Length > 0)
{
if (line.StartsWith("remote: Enumerating objects:", StringComparison.Ordinal) ||
line.StartsWith("remote: Counting objects:", StringComparison.Ordinal) ||
line.StartsWith("remote: Compressing objects:", StringComparison.Ordinal) ||
line.StartsWith("Filtering content:", StringComparison.Ordinal) ||
line.StartsWith("hint:", StringComparison.Ordinal))
return;
if (REG_PROGRESS().IsMatch(line))
return;
}
errs.Add(line);
}
[GeneratedRegex(@"\d+%")]
private static partial Regex REG_PROGRESS();
}

View file

@ -4,19 +4,18 @@ namespace SourceGit.Commands
{
public class Commit : Command
{
public Commit(string repo, string message, bool amend, bool signOff)
public Commit(string repo, string message, bool signOff, bool amend, bool resetAuthor)
{
_tmpFile = Path.GetTempFileName();
File.WriteAllText(_tmpFile, message);
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
Args = $"commit --allow-empty --file=\"{_tmpFile}\"";
if (amend)
Args += " --amend --no-edit";
if (signOff)
Args += " --signoff";
if (amend)
Args += resetAuthor ? " --amend --reset-author --no-edit" : " --amend --no-edit";
}
public bool Run()
@ -35,6 +34,6 @@ namespace SourceGit.Commands
return succ;
}
private string _tmpFile = string.Empty;
private readonly string _tmpFile;
}
}

View file

@ -6,8 +6,10 @@ namespace SourceGit.Commands
{
public partial class CompareRevisions : Command
{
[GeneratedRegex(@"^(\s?[\w\?]{1,4})\s+(.+)$")]
[GeneratedRegex(@"^([MADC])\s+(.+)$")]
private static partial Regex REG_FORMAT();
[GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)$")]
private static partial Regex REG_RENAME_FORMAT();
public CompareRevisions(string repo, string start, string end)
{
@ -29,16 +31,33 @@ namespace SourceGit.Commands
public List<Models.Change> Result()
{
Exec();
_changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal));
var rs = ReadToEnd();
if (!rs.IsSuccess)
return _changes;
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
ParseLine(line);
_changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path));
return _changes;
}
protected override void OnReadline(string line)
private void ParseLine(string line)
{
var match = REG_FORMAT().Match(line);
if (!match.Success)
{
match = REG_RENAME_FORMAT().Match(line);
if (match.Success)
{
var renamed = new Models.Change() { Path = match.Groups[1].Value };
renamed.Set(Models.ChangeState.Renamed);
_changes.Add(renamed);
}
return;
}
var change = new Models.Change() { Path = match.Groups[2].Value };
var status = match.Groups[1].Value;
@ -57,10 +76,6 @@ namespace SourceGit.Commands
change.Set(Models.ChangeState.Deleted);
_changes.Add(change);
break;
case 'R':
change.Set(Models.ChangeState.Renamed);
_changes.Add(change);
break;
case 'C':
change.Set(Models.ChangeState.Copied);
_changes.Add(change);

View file

@ -29,7 +29,7 @@ namespace SourceGit.Commands
var rs = new Dictionary<string, string>();
if (output.IsSuccess)
{
var lines = output.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
var lines = output.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var idx = line.IndexOf('=', StringComparison.Ordinal);

View file

@ -8,7 +8,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = "status -uno --ignore-submodules=dirty --porcelain";
Args = "--no-optional-locks status -uno --ignore-submodules=all --porcelain";
}
public int Result()
@ -16,7 +16,7 @@ namespace SourceGit.Commands
var rs = ReadToEnd();
if (rs.IsSuccess)
{
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
return lines.Length;
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
@ -28,34 +28,48 @@ namespace SourceGit.Commands
Context = repo;
if (ignoreWhitespace)
Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-cr-at-eol --ignore-all-space --unified={unified} {opt}";
Args = $"diff --no-ext-diff --patch --ignore-all-space --unified={unified} {opt}";
else if (Models.DiffOption.IgnoreCRAtEOL)
Args = $"diff --no-ext-diff --patch --ignore-cr-at-eol --unified={unified} {opt}";
else
Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-cr-at-eol --unified={unified} {opt}";
Args = $"diff --no-ext-diff --patch --unified={unified} {opt}";
}
public Models.DiffResult Result()
{
Exec();
var rs = ReadToEnd();
var start = 0;
var end = rs.StdOut.IndexOf('\n', start);
while (end > 0)
{
var line = rs.StdOut.Substring(start, end - start);
ParseLine(line);
if (_result.IsBinary || _result.IsLFS)
start = end + 1;
end = rs.StdOut.IndexOf('\n', start);
}
if (start < rs.StdOut.Length)
ParseLine(rs.StdOut.Substring(start));
if (_result.IsBinary || _result.IsLFS || _result.TextDiff.Lines.Count == 0)
{
_result.TextDiff = null;
}
else
{
ProcessInlineHighlights();
if (_result.TextDiff.Lines.Count == 0)
_result.TextDiff = null;
else
_result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine);
_result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine);
}
return _result;
}
protected override void OnReadline(string line)
private void ParseLine(string line)
{
if (_result.IsBinary)
return;
if (line.StartsWith("old mode ", StringComparison.Ordinal))
{
_result.OldMode = line.Substring(9);
@ -68,8 +82,17 @@ namespace SourceGit.Commands
return;
}
if (_result.IsBinary)
if (line.StartsWith("deleted file mode ", StringComparison.Ordinal))
{
_result.OldMode = line.Substring(18);
return;
}
if (line.StartsWith("new file mode ", StringComparison.Ordinal))
{
_result.NewMode = line.Substring(14);
return;
}
if (_result.IsLFS)
{
@ -82,7 +105,7 @@ namespace SourceGit.Commands
}
else if (line.StartsWith("-size ", StringComparison.Ordinal))
{
_result.LFSDiff.Old.Size = long.Parse(line.Substring(6));
_result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
}
}
else if (ch == '+')
@ -93,12 +116,12 @@ namespace SourceGit.Commands
}
else if (line.StartsWith("+size ", StringComparison.Ordinal))
{
_result.LFSDiff.New.Size = long.Parse(line.Substring(6));
_result.LFSDiff.New.Size = long.Parse(line.AsSpan(6));
}
}
else if (line.StartsWith(" size ", StringComparison.Ordinal))
{
_result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.Substring(6));
_result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
}
return;
}
@ -128,7 +151,8 @@ namespace SourceGit.Commands
_oldLine = int.Parse(match.Groups[1].Value);
_newLine = int.Parse(match.Groups[2].Value);
_result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0));
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
_result.TextDiff.Lines.Add(_last);
}
}
else
@ -136,7 +160,8 @@ namespace SourceGit.Commands
if (line.Length == 0)
{
ProcessInlineHighlights();
_result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", _oldLine, _newLine));
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", _oldLine, _newLine);
_result.TextDiff.Lines.Add(_last);
_oldLine++;
_newLine++;
return;
@ -152,7 +177,8 @@ namespace SourceGit.Commands
return;
}
_deleted.Add(new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), _oldLine, 0));
_last = new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), _oldLine, 0);
_deleted.Add(_last);
_oldLine++;
}
else if (ch == '+')
@ -164,7 +190,8 @@ namespace SourceGit.Commands
return;
}
_added.Add(new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), 0, _newLine));
_last = new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), 0, _newLine);
_added.Add(_last);
_newLine++;
}
else if (ch != '\\')
@ -175,7 +202,8 @@ namespace SourceGit.Commands
{
_oldLine = int.Parse(match.Groups[1].Value);
_newLine = int.Parse(match.Groups[2].Value);
_result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0));
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
_result.TextDiff.Lines.Add(_last);
}
else
{
@ -186,11 +214,16 @@ namespace SourceGit.Commands
return;
}
_result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), _oldLine, _newLine));
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), _oldLine, _newLine);
_result.TextDiff.Lines.Add(_last);
_oldLine++;
_newLine++;
}
}
else if (line.Equals("\\ No newline at end of file", StringComparison.Ordinal))
{
_last.NoNewLineEndOfFile = true;
}
}
}
@ -241,6 +274,7 @@ namespace SourceGit.Commands
private readonly Models.DiffResult _result = new Models.DiffResult();
private readonly List<Models.TextDiffLine> _deleted = new List<Models.TextDiffLine>();
private readonly List<Models.TextDiffLine> _added = new List<Models.TextDiffLine>();
private Models.TextDiffLine _last = null;
private int _oldLine = 0;
private int _newLine = 0;
}

View file

@ -1,39 +1,95 @@
using System;
using System.Collections.Generic;
using System.IO;
using Avalonia.Threading;
namespace SourceGit.Commands
{
public static class Discard
{
public static void All(string repo, bool includeIgnored)
/// <summary>
/// Discard all local changes (unstaged & staged)
/// </summary>
/// <param name="repo"></param>
/// <param name="includeIgnored"></param>
/// <param name="log"></param>
public static void All(string repo, bool includeIgnored, Models.ICommandLog log)
{
new Restore(repo).Exec();
new Clean(repo, includeIgnored).Exec();
var changes = new QueryLocalChanges(repo).Result();
try
{
foreach (var c in changes)
{
if (c.WorkTree == Models.ChangeState.Untracked ||
c.WorkTree == Models.ChangeState.Added ||
c.Index == Models.ChangeState.Added ||
c.Index == Models.ChangeState.Renamed)
{
var fullPath = Path.Combine(repo, c.Path);
if (Directory.Exists(fullPath))
Directory.Delete(fullPath, true);
else
File.Delete(fullPath);
}
}
}
catch (Exception e)
{
Dispatcher.UIThread.Invoke(() =>
{
App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}");
});
}
new Reset(repo, "HEAD", "--hard") { Log = log }.Exec();
if (includeIgnored)
new Clean(repo) { Log = log }.Exec();
}
public static void Changes(string repo, List<Models.Change> changes)
/// <summary>
/// Discard selected changes (only unstaged).
/// </summary>
/// <param name="repo"></param>
/// <param name="changes"></param>
/// <param name="log"></param>
public static void Changes(string repo, List<Models.Change> changes, Models.ICommandLog log)
{
var needClean = new List<string>();
var needCheckout = new List<string>();
var restores = new List<string>();
foreach (var c in changes)
try
{
if (c.WorkTree == Models.ChangeState.Untracked || c.WorkTree == Models.ChangeState.Added)
needClean.Add(c.Path);
else
needCheckout.Add(c.Path);
foreach (var c in changes)
{
if (c.WorkTree == Models.ChangeState.Untracked || c.WorkTree == Models.ChangeState.Added)
{
var fullPath = Path.Combine(repo, c.Path);
if (Directory.Exists(fullPath))
Directory.Delete(fullPath, true);
else
File.Delete(fullPath);
}
else
{
restores.Add(c.Path);
}
}
}
catch (Exception e)
{
Dispatcher.UIThread.Invoke(() =>
{
App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}");
});
}
for (int i = 0; i < needClean.Count; i += 10)
if (restores.Count > 0)
{
var count = Math.Min(10, needClean.Count - i);
new Clean(repo, needClean.GetRange(i, count)).Exec();
}
for (int i = 0; i < needCheckout.Count; i += 10)
{
var count = Math.Min(10, needCheckout.Count - i);
new Restore(repo, needCheckout.GetRange(i, count), "--worktree --recurse-submodules").Exec();
var pathSpecFile = Path.GetTempFileName();
File.WriteAllLines(pathSpecFile, restores);
new Restore(repo, pathSpecFile, false) { Log = log }.Exec();
File.Delete(pathSpecFile);
}
}
}

View file

@ -17,14 +17,6 @@ namespace SourceGit.Commands
start.CreateNoWindow = true;
start.WorkingDirectory = repo;
// Force using en_US.UTF-8 locale to avoid GCM crash
if (OperatingSystem.IsLinux())
start.Environment.Add("LANG", "en_US.UTF-8");
// Fix macOS `PATH` env
if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv))
start.Environment.Add("PATH", Native.OS.CustomPathEnv);
try
{
Process.Start(start);
@ -35,7 +27,7 @@ namespace SourceGit.Commands
}
}
public static void RunAndWait(string repo, string file, string args, Action<string> outputHandler)
public static void RunAndWait(string repo, string file, string args, Models.ICommandLog log)
{
var start = new ProcessStartInfo();
start.FileName = file;
@ -48,13 +40,7 @@ namespace SourceGit.Commands
start.StandardErrorEncoding = Encoding.UTF8;
start.WorkingDirectory = repo;
// Force using en_US.UTF-8 locale to avoid GCM crash
if (OperatingSystem.IsLinux())
start.Environment.Add("LANG", "en_US.UTF-8");
// Fix macOS `PATH` env
if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv))
start.Environment.Add("PATH", Native.OS.CustomPathEnv);
log?.AppendLine($"$ {file} {args}\n");
var proc = new Process() { StartInfo = start };
var builder = new StringBuilder();
@ -62,14 +48,14 @@ namespace SourceGit.Commands
proc.OutputDataReceived += (_, e) =>
{
if (e.Data != null)
outputHandler?.Invoke(e.Data);
log?.AppendLine(e.Data);
};
proc.ErrorDataReceived += (_, e) =>
{
if (e.Data != null)
{
outputHandler?.Invoke(e.Data);
log?.AppendLine(e.Data);
builder.AppendLine(e.Data);
}
};

View file

@ -1,15 +1,11 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Fetch : Command
{
public Fetch(string repo, string remote, bool noTags, bool force, Action<string> outputHandler)
public Fetch(string repo, string remote, bool noTags, bool force)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "fetch --progress --verbose ";
@ -24,21 +20,12 @@ namespace SourceGit.Commands
Args += remote;
}
public Fetch(string repo, Models.Branch local, Models.Branch remote, Action<string> outputHandler)
public Fetch(string repo, Models.Branch local, Models.Branch remote)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote.Remote}.sshkey");
Args = $"fetch --progress --verbose {remote.Remote} {remote.Name}:{local.Name}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -6,6 +6,7 @@
{
WorkingDirectory = repo;
Context = repo;
Editor = EditorType.None;
Args = $"format-patch {commit} -1 --output=\"{saveTo}\"";
}
}

View file

@ -1,23 +1,12 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class GC : Command
{
public GC(string repo, Action<string> outputHandler)
public GC(string repo)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
Args = "gc --prune=now";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -1,52 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Threading;
namespace SourceGit.Commands
{
public static class GitFlow
{
public class BranchDetectResult
public static bool Init(string repo, string master, string develop, string feature, string release, string hotfix, string version, Models.ICommandLog log)
{
public bool IsGitFlowBranch { get; set; } = false;
public string Type { get; set; } = string.Empty;
public string Prefix { get; set; } = string.Empty;
}
public static bool IsEnabled(string repo, List<Models.Branch> branches)
{
var localBrancheNames = new HashSet<string>();
foreach (var branch in branches)
{
if (branch.IsLocal)
localBrancheNames.Add(branch.Name);
}
var config = new Config(repo).ListAll();
if (!config.TryGetValue("gitflow.branch.master", out string master) || !localBrancheNames.Contains(master))
return false;
if (!config.TryGetValue("gitflow.branch.develop", out string develop) || !localBrancheNames.Contains(develop))
return false;
return config.ContainsKey("gitflow.prefix.feature") &&
config.ContainsKey("gitflow.prefix.release") &&
config.ContainsKey("gitflow.prefix.hotfix");
}
public static bool Init(string repo, List<Models.Branch> branches, string master, string develop, string feature, string release, string hotfix, string version)
{
var current = branches.Find(x => x.IsCurrent);
var masterBranch = branches.Find(x => x.Name == master);
if (masterBranch == null && current != null)
Branch.Create(repo, master, current.Head);
var devBranch = branches.Find(x => x.Name == develop);
if (devBranch == null && current != null)
Branch.Create(repo, develop, current.Head);
var config = new Config(repo);
config.Set("gitflow.branch.master", master);
config.Set("gitflow.branch.develop", develop);
@ -61,104 +21,72 @@ namespace SourceGit.Commands
init.WorkingDirectory = repo;
init.Context = repo;
init.Args = "flow init -d";
init.Log = log;
return init.Exec();
}
public static string GetPrefix(string repo, string type)
public static bool Start(string repo, Models.GitFlowBranchType type, string name, Models.ICommandLog log)
{
return new Config(repo).Get($"gitflow.prefix.{type}");
}
public static BranchDetectResult DetectType(string repo, List<Models.Branch> branches, string branch)
{
var rs = new BranchDetectResult();
var localBrancheNames = new HashSet<string>();
foreach (var b in branches)
{
if (b.IsLocal)
localBrancheNames.Add(b.Name);
}
var config = new Config(repo).ListAll();
if (!config.TryGetValue("gitflow.branch.master", out string master) || !localBrancheNames.Contains(master))
return rs;
if (!config.TryGetValue("gitflow.branch.develop", out string develop) || !localBrancheNames.Contains(develop))
return rs;
if (!config.TryGetValue("gitflow.prefix.feature", out var feature) ||
!config.TryGetValue("gitflow.prefix.release", out var release) ||
!config.TryGetValue("gitflow.prefix.hotfix", out var hotfix))
return rs;
if (branch.StartsWith(feature, StringComparison.Ordinal))
{
rs.IsGitFlowBranch = true;
rs.Type = "feature";
rs.Prefix = feature;
}
else if (branch.StartsWith(release, StringComparison.Ordinal))
{
rs.IsGitFlowBranch = true;
rs.Type = "release";
rs.Prefix = release;
}
else if (branch.StartsWith(hotfix, StringComparison.Ordinal))
{
rs.IsGitFlowBranch = true;
rs.Type = "hotfix";
rs.Prefix = hotfix;
}
return rs;
}
public static bool Start(string repo, string type, string name)
{
if (!SUPPORTED_BRANCH_TYPES.Contains(type))
{
Dispatcher.UIThread.Post(() =>
{
App.RaiseException(repo, "Bad branch type!!!");
});
return false;
}
var start = new Command();
start.WorkingDirectory = repo;
start.Context = repo;
start.Args = $"flow {type} start {name}";
switch (type)
{
case Models.GitFlowBranchType.Feature:
start.Args = $"flow feature start {name}";
break;
case Models.GitFlowBranchType.Release:
start.Args = $"flow release start {name}";
break;
case Models.GitFlowBranchType.Hotfix:
start.Args = $"flow hotfix start {name}";
break;
default:
Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, "Bad git-flow branch type!!!"));
return false;
}
start.Log = log;
return start.Exec();
}
public static bool Finish(string repo, string type, string name, bool keepBranch)
public static bool Finish(string repo, Models.GitFlowBranchType type, string name, bool squash, bool push, bool keepBranch, Models.ICommandLog log)
{
if (!SUPPORTED_BRANCH_TYPES.Contains(type))
{
Dispatcher.UIThread.Post(() =>
{
App.RaiseException(repo, "Bad branch type!!!");
});
var builder = new StringBuilder();
builder.Append("flow ");
return false;
switch (type)
{
case Models.GitFlowBranchType.Feature:
builder.Append("feature");
break;
case Models.GitFlowBranchType.Release:
builder.Append("release");
break;
case Models.GitFlowBranchType.Hotfix:
builder.Append("hotfix");
break;
default:
Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, "Bad git-flow branch type!!!"));
return false;
}
var option = keepBranch ? "-k" : string.Empty;
builder.Append(" finish ");
if (squash)
builder.Append("--squash ");
if (push)
builder.Append("--push ");
if (keepBranch)
builder.Append("-k ");
builder.Append(name);
var finish = new Command();
finish.WorkingDirectory = repo;
finish.Context = repo;
finish.Args = $"flow {type} finish {option} {name}";
finish.Args = builder.ToString();
finish.Log = log;
return finish.Exec();
}
private static readonly List<string> SUPPORTED_BRANCH_TYPES = new List<string>()
{
"feature",
"release",
"bugfix",
"hotfix",
"support",
};
}
}

View file

@ -8,7 +8,14 @@ namespace SourceGit.Commands
{
var file = Path.Combine(repo, ".gitignore");
if (!File.Exists(file))
{
File.WriteAllLines(file, [pattern]);
return;
}
var org = File.ReadAllText(file);
if (!org.EndsWith('\n'))
File.AppendAllLines(file, ["", pattern]);
else
File.AppendAllLines(file, [pattern]);
}

View file

@ -11,7 +11,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = $"diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 {commit} --numstat -- \"{path}\"";
Args = $"diff {Models.Commit.EmptyTreeSHA1} {commit} --numstat -- \"{path}\"";
RaiseError = false;
}

View file

@ -0,0 +1,17 @@
namespace SourceGit.Commands
{
public class IsCommitSHA : Command
{
public IsCommitSHA(string repo, string hash)
{
WorkingDirectory = repo;
Args = $"cat-file -t {hash}";
}
public bool Result()
{
var rs = ReadToEnd();
return rs.IsSuccess && rs.StdOut.Trim().Equals("commit");
}
}
}

View file

@ -10,5 +10,10 @@
Context = repo;
Args = $"diff -a --ignore-cr-at-eol --check {opt}";
}
public bool Result()
{
return ReadToEnd().IsSuccess;
}
}
}

View file

@ -10,23 +10,15 @@ namespace SourceGit.Commands
[GeneratedRegex(@"^(.+)\s+([\w.]+)\s+\w+:(\d+)$")]
private static partial Regex REG_LOCK();
class SubCmd : Command
private class SubCmd : Command
{
public SubCmd(string repo, string args, Action<string> onProgress)
public SubCmd(string repo, string args, Models.ICommandLog log)
{
WorkingDirectory = repo;
Context = repo;
Args = args;
TraitErrorAsOutput = true;
_outputHandler = onProgress;
Log = log;
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
public LFS(string repo)
@ -44,35 +36,35 @@ namespace SourceGit.Commands
return content.Contains("git lfs pre-push");
}
public bool Install()
public bool Install(Models.ICommandLog log)
{
return new SubCmd(_repo, "lfs install --local", null).Exec();
return new SubCmd(_repo, "lfs install --local", log).Exec();
}
public bool Track(string pattern, bool isFilenameMode = false)
public bool Track(string pattern, bool isFilenameMode, Models.ICommandLog log)
{
var opt = isFilenameMode ? "--filename" : "";
return new SubCmd(_repo, $"lfs track {opt} \"{pattern}\"", null).Exec();
return new SubCmd(_repo, $"lfs track {opt} \"{pattern}\"", log).Exec();
}
public void Fetch(string remote, Action<string> outputHandler)
public void Fetch(string remote, Models.ICommandLog log)
{
new SubCmd(_repo, $"lfs fetch {remote}", outputHandler).Exec();
new SubCmd(_repo, $"lfs fetch {remote}", log).Exec();
}
public void Pull(string remote, Action<string> outputHandler)
public void Pull(string remote, Models.ICommandLog log)
{
new SubCmd(_repo, $"lfs pull {remote}", outputHandler).Exec();
new SubCmd(_repo, $"lfs pull {remote}", log).Exec();
}
public void Push(string remote, Action<string> outputHandler)
public void Push(string remote, Models.ICommandLog log)
{
new SubCmd(_repo, $"lfs push {remote}", outputHandler).Exec();
new SubCmd(_repo, $"lfs push {remote}", log).Exec();
}
public void Prune(Action<string> outputHandler)
public void Prune(Models.ICommandLog log)
{
new SubCmd(_repo, "lfs prune", outputHandler).Exec();
new SubCmd(_repo, "lfs prune", log).Exec();
}
public List<Models.LFSLock> Locks(string remote)
@ -82,7 +74,7 @@ namespace SourceGit.Commands
var rs = cmd.ReadToEnd();
if (rs.IsSuccess)
{
var lines = rs.StdOut.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_LOCK().Match(line);
@ -101,21 +93,21 @@ namespace SourceGit.Commands
return locks;
}
public bool Lock(string remote, string file)
public bool Lock(string remote, string file, Models.ICommandLog log)
{
return new SubCmd(_repo, $"lfs lock --remote={remote} \"{file}\"", null).Exec();
return new SubCmd(_repo, $"lfs lock --remote={remote} \"{file}\"", log).Exec();
}
public bool Unlock(string remote, string file, bool force)
public bool Unlock(string remote, string file, bool force, Models.ICommandLog log)
{
var opt = force ? "-f" : "";
return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} \"{file}\"", null).Exec();
return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} \"{file}\"", log).Exec();
}
public bool Unlock(string remote, long id, bool force)
public bool Unlock(string remote, long id, bool force, Models.ICommandLog log)
{
var opt = force ? "-f" : "";
return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} --id={id}", null).Exec();
return new SubCmd(_repo, $"lfs unlock --remote={remote} {opt} --id={id}", log).Exec();
}
private readonly string _repo;

View file

@ -1,26 +1,30 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
{
public class Merge : Command
{
public Merge(string repo, string source, string mode, Action<string> outputHandler)
public Merge(string repo, string source, string mode, bool edit)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
Args = $"merge --progress {source} {mode}";
Editor = EditorType.CoreEditor;
var builder = new StringBuilder();
builder.Append("merge --progress ");
builder.Append(edit ? "--edit " : "--no-edit ");
builder.Append(source);
builder.Append(' ');
builder.Append(mode);
Args = builder.ToString();
}
public Merge(string repo, List<string> targets, bool autoCommit, string strategy, Action<string> outputHandler)
public Merge(string repo, List<string> targets, bool autoCommit, string strategy)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
var builder = new StringBuilder();
builder.Append("merge --progress ");
@ -37,12 +41,5 @@ namespace SourceGit.Commands
Args = builder.ToString();
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler = null;
}
}

View file

@ -13,15 +13,18 @@ namespace SourceGit.Commands
cmd.Context = repo;
cmd.RaiseError = true;
// NOTE: If no <file> names are specified, 'git mergetool' will run the merge tool program on every file with merge conflicts.
var fileArg = string.IsNullOrEmpty(file) ? "" : $"\"{file}\"";
if (toolType == 0)
{
cmd.Args = $"mergetool \"{file}\"";
cmd.Args = $"mergetool {fileArg}";
return cmd.Exec();
}
if (!File.Exists(toolPath))
{
Dispatcher.UIThread.Post(() => App.RaiseException(repo, $"Can NOT found external merge tool in '{toolPath}'!"));
Dispatcher.UIThread.Post(() => App.RaiseException(repo, $"Can NOT find external merge tool in '{toolPath}'!"));
return false;
}
@ -32,7 +35,7 @@ namespace SourceGit.Commands
return false;
}
cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{toolPath}\\\" {supported.Cmd}\" -c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true mergetool --tool=sourcegit \"{file}\"";
cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{toolPath}\\\" {supported.Cmd}\" -c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true mergetool --tool=sourcegit {fileArg}";
return cmd.Exec();
}
@ -51,7 +54,7 @@ namespace SourceGit.Commands
if (!File.Exists(toolPath))
{
Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, $"Can NOT found external diff tool in '{toolPath}'!"));
Dispatcher.UIThread.Invoke(() => App.RaiseException(repo, $"Can NOT find external diff tool in '{toolPath}'!"));
return false;
}

View file

@ -1,31 +1,18 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Pull : Command
{
public Pull(string repo, string remote, string branch, bool useRebase, bool noTags, Action<string> outputHandler)
public Pull(string repo, string remote, string branch, bool useRebase)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "pull --verbose --progress --tags ";
Args = "pull --verbose --progress ";
if (useRebase)
Args += "--rebase ";
if (noTags)
Args += "--no-tags ";
Args += "--rebase=true ";
Args += $"{remote} {branch}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -1,16 +1,11 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Push : Command
{
public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force, Action<string> onProgress)
public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force)
{
_outputHandler = onProgress;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "push --progress --verbose ";
@ -26,7 +21,7 @@ namespace SourceGit.Commands
Args += $"{remote} {local}:{remoteBranch}";
}
public Push(string repo, string remote, string tag, bool isDelete)
public Push(string repo, string remote, string refname, bool isDelete)
{
WorkingDirectory = repo;
Context = repo;
@ -36,14 +31,7 @@ namespace SourceGit.Commands
if (isDelete)
Args += "--delete ";
Args += $"{remote} refs/tags/{tag}";
Args += $"{remote} {refname}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler = null;
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
{
public partial class QueryAssumeUnchangedFiles : Command
{
[GeneratedRegex(@"^(\w)\s+(.+)$")]
private static partial Regex REG_PARSE();
public QueryAssumeUnchangedFiles(string repo)
{
WorkingDirectory = repo;
Args = "ls-files -v";
RaiseError = false;
}
public List<string> Result()
{
var outs = new List<string>();
var rs = ReadToEnd();
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_PARSE().Match(line);
if (!match.Success)
continue;
if (match.Groups[1].Value == "h")
outs.Add(match.Groups[2].Value);
}
return outs;
}
}
}

View file

@ -14,18 +14,20 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = "branch -l --all -v --format=\"%(refname)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)\"";
Args = "branch -l --all -v --format=\"%(refname)%00%(committerdate:unix)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)\"";
}
public List<Models.Branch> Result()
public List<Models.Branch> Result(out int localBranchesCount)
{
localBranchesCount = 0;
var branches = new List<Models.Branch>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return branches;
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
var remoteBranches = new HashSet<string>();
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
var remoteHeads = new Dictionary<string, string>();
foreach (var line in lines)
{
var b = ParseLine(line);
@ -33,14 +35,27 @@ namespace SourceGit.Commands
{
branches.Add(b);
if (!b.IsLocal)
remoteBranches.Add(b.FullName);
remoteHeads.Add(b.FullName, b.Head);
else
localBranchesCount++;
}
}
foreach (var b in branches)
{
if (b.IsLocal && !string.IsNullOrEmpty(b.Upstream))
b.IsUpsteamGone = !remoteBranches.Contains(b.Upstream);
{
if (remoteHeads.TryGetValue(b.Upstream, out var upstreamHead))
{
b.IsUpstreamGone = false;
b.TrackStatus ??= new QueryTrackStatus(WorkingDirectory, b.Head, upstreamHead).Result();
}
else
{
b.IsUpstreamGone = true;
b.TrackStatus ??= new Models.BranchTrackStatus();
}
}
}
return branches;
@ -49,7 +64,7 @@ namespace SourceGit.Commands
private Models.Branch ParseLine(string line)
{
var parts = line.Split('\0');
if (parts.Length != 5)
if (parts.Length != 6)
return null;
var branch = new Models.Branch();
@ -83,14 +98,16 @@ namespace SourceGit.Commands
}
branch.FullName = refName;
branch.Head = parts[1];
branch.IsCurrent = parts[2] == "*";
branch.Upstream = parts[3];
branch.IsUpsteamGone = false;
branch.CommitterDate = ulong.Parse(parts[1]);
branch.Head = parts[2];
branch.IsCurrent = parts[3] == "*";
branch.Upstream = parts[4];
branch.IsUpstreamGone = false;
if (branch.IsLocal && !string.IsNullOrEmpty(parts[4]) && !parts[4].Equals("=", StringComparison.Ordinal))
branch.TrackStatus = new QueryTrackStatus(WorkingDirectory, branch.Name, branch.Upstream).Result();
else
if (!branch.IsLocal ||
string.IsNullOrEmpty(branch.Upstream) ||
string.IsNullOrEmpty(parts[5]) ||
parts[5].Equals("=", StringComparison.Ordinal))
branch.TrackStatus = new Models.BranchTrackStatus();
return branch;

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace SourceGit.Commands
{
@ -9,22 +10,26 @@ namespace SourceGit.Commands
WorkingDirectory = repo;
Context = repo;
_commit = commit;
Args = $"rev-list -{max} --parents --branches --remotes ^{commit}";
Args = $"rev-list -{max} --parents --branches --remotes --ancestry-path ^{commit}";
}
public IEnumerable<string> Result()
public List<string> Result()
{
Exec();
return _lines;
}
var rs = ReadToEnd();
var outs = new List<string>();
if (rs.IsSuccess)
{
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line.Contains(_commit))
outs.Add(line.Substring(0, 40));
}
}
protected override void OnReadline(string line)
{
if (line.Contains(_commit))
_lines.Add(line.Substring(0, 40));
return outs;
}
private string _commit;
private List<string> _lines = new List<string>();
}
}

View file

@ -6,7 +6,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = $"show --no-show-signature --pretty=format:%B -s {sha}";
Args = $"show --no-show-signature --format=%B -s {sha}";
}
public string Result()

View file

@ -7,7 +7,7 @@
WorkingDirectory = repo;
Context = repo;
const string baseArgs = "show --no-show-signature --pretty=format:\"%G?%n%GS%n%GK\" -s";
const string baseArgs = "show --no-show-signature --format=%G?%n%GS%n%GK -s";
const string fakeSignersFileArg = "-c gpg.ssh.allowedSignersFile=/dev/null";
Args = $"{(useFakeSignersFile ? fakeSignersFileArg : string.Empty)} {baseArgs} {sha}";
}
@ -18,7 +18,7 @@
if (!rs.IsSuccess)
return null;
var raw = rs.StdOut.Trim();
var raw = rs.StdOut.Trim().ReplaceLineEndings("\n");
if (raw.Length <= 1)
return null;
@ -29,7 +29,6 @@
Signer = lines[1],
Key = lines[2]
};
}
}
}

View file

@ -10,7 +10,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = $"log --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {limits}";
Args = $"log --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {limits}";
_findFirstMerged = needFindHead;
}
@ -26,16 +26,12 @@ namespace SourceGit.Commands
{
search += $"-i --committer=\"{filter}\"";
}
else if (method == Models.CommitSearchMethod.ByFile)
{
search += $"-- \"{filter}\"";
}
else
else if (method == Models.CommitSearchMethod.ByMessage)
{
var argsBuilder = new StringBuilder();
argsBuilder.Append(search);
var words = filter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var words = filter.Split([' ', '\t', '\r'], StringSplitOptions.RemoveEmptyEntries);
foreach (var word in words)
{
var escaped = word.Trim().Replace("\"", "\\\"", StringComparison.Ordinal);
@ -45,10 +41,18 @@ namespace SourceGit.Commands
search = argsBuilder.ToString();
}
else if (method == Models.CommitSearchMethod.ByFile)
{
search += $"-- \"{filter}\"";
}
else
{
search = $"-G\"{filter}\"";
}
WorkingDirectory = repo;
Context = repo;
Args = $"log -1000 --date-order --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s " + search;
Args = $"log -1000 --date-order --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {search}";
_findFirstMerged = false;
}
@ -124,7 +128,7 @@ namespace SourceGit.Commands
Args = $"log --since=\"{_commits[^1].CommitterTimeStr}\" --format=\"%H\"";
var rs = ReadToEnd();
var shas = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
var shas = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
if (shas.Length == 0)
return;

View file

@ -3,18 +3,18 @@ using System.Collections.Generic;
namespace SourceGit.Commands
{
public class QueryCommitsWithFullMessage : Command
public class QueryCommitsForInteractiveRebase : Command
{
public QueryCommitsWithFullMessage(string repo, string args)
public QueryCommitsForInteractiveRebase(string repo, string on)
{
_boundary = $"----- BOUNDARY OF COMMIT {Guid.NewGuid()} -----";
WorkingDirectory = repo;
Context = repo;
Args = $"log --date-order --no-show-signature --decorate=full --pretty=format:\"%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%B%n{_boundary}\" {args}";
Args = $"log --date-order --no-show-signature --decorate=full --format=\"%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%B%n{_boundary}\" {on}..HEAD";
}
public List<Models.CommitWithMessage> Result()
public List<Models.InteractiveCommit> Result()
{
var rs = ReadToEnd();
if (!rs.IsSuccess)
@ -29,7 +29,7 @@ namespace SourceGit.Commands
switch (nextPartIdx)
{
case 0:
_current = new Models.CommitWithMessage();
_current = new Models.InteractiveCommit();
_current.Commit.SHA = line;
_commits.Add(_current);
break;
@ -52,7 +52,7 @@ namespace SourceGit.Commands
_current.Commit.CommitterTime = ulong.Parse(line);
break;
default:
var boundary = rs.StdOut.IndexOf(_boundary, end + 1);
var boundary = rs.StdOut.IndexOf(_boundary, end + 1, StringComparison.Ordinal);
if (boundary > end)
{
_current.Message = rs.StdOut.Substring(start, boundary - start - 1);
@ -88,8 +88,8 @@ namespace SourceGit.Commands
_current.Commit.Parents.AddRange(data.Split(separator: ' ', options: StringSplitOptions.RemoveEmptyEntries));
}
private List<Models.CommitWithMessage> _commits = new List<Models.CommitWithMessage>();
private Models.CommitWithMessage _current = null;
private string _boundary = "";
private List<Models.InteractiveCommit> _commits = [];
private Models.InteractiveCommit _current = null;
private readonly string _boundary;
}
}

View file

@ -35,5 +35,39 @@ namespace SourceGit.Commands
return stream;
}
public static Stream FromLFS(string repo, string oid, long size)
{
var starter = new ProcessStartInfo();
starter.WorkingDirectory = repo;
starter.FileName = Native.OS.GitExecutable;
starter.Arguments = $"lfs smudge";
starter.UseShellExecute = false;
starter.CreateNoWindow = true;
starter.WindowStyle = ProcessWindowStyle.Hidden;
starter.RedirectStandardInput = true;
starter.RedirectStandardOutput = true;
var stream = new MemoryStream();
try
{
var proc = new Process() { StartInfo = starter };
proc.Start();
proc.StandardInput.WriteLine("version https://git-lfs.github.com/spec/v1");
proc.StandardInput.WriteLine($"oid sha256:{oid}");
proc.StandardInput.WriteLine($"size {size}");
proc.StandardOutput.BaseStream.CopyTo(stream);
proc.WaitForExit();
proc.Close();
stream.Position = 0;
}
catch (Exception e)
{
App.RaiseException(repo, $"Failed to query file content: {e}");
}
return stream;
}
}
}

View file

@ -16,9 +16,6 @@ namespace SourceGit.Commands
public long Result()
{
if (_result != 0)
return _result;
var rs = ReadToEnd();
if (rs.IsSuccess)
{
@ -29,7 +26,5 @@ namespace SourceGit.Commands
return 0;
}
private readonly long _result = 0;
}
}

View file

@ -0,0 +1,26 @@
using System.IO;
namespace SourceGit.Commands
{
public class QueryGitCommonDir : Command
{
public QueryGitCommonDir(string workDir)
{
WorkingDirectory = workDir;
Args = "rev-parse --git-common-dir";
RaiseError = false;
}
public string Result()
{
var rs = ReadToEnd().StdOut;
if (string.IsNullOrEmpty(rs))
return null;
rs = rs.Trim();
if (Path.IsPathRooted(rs))
return rs;
return Path.GetFullPath(Path.Combine(WorkingDirectory, rs));
}
}
}

View file

@ -1,6 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Avalonia.Threading;
namespace SourceGit.Commands
{
public partial class QueryLocalChanges : Command
@ -13,144 +16,150 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = $"status -u{UNTRACKED[includeUntracked ? 1 : 0]} --ignore-submodules=dirty --porcelain";
Args = $"--no-optional-locks status -u{UNTRACKED[includeUntracked ? 1 : 0]} --ignore-submodules=dirty --porcelain";
}
public List<Models.Change> Result()
{
Exec();
return _changes;
}
protected override void OnReadline(string line)
{
var match = REG_FORMAT().Match(line);
if (!match.Success)
return;
var change = new Models.Change() { Path = match.Groups[2].Value };
var status = match.Groups[1].Value;
switch (status)
var outs = new List<Models.Change>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
{
case " M":
change.Set(Models.ChangeState.None, Models.ChangeState.Modified);
break;
case " T":
change.Set(Models.ChangeState.None, Models.ChangeState.TypeChanged);
break;
case " A":
change.Set(Models.ChangeState.None, Models.ChangeState.Added);
break;
case " D":
change.Set(Models.ChangeState.None, Models.ChangeState.Deleted);
break;
case " R":
change.Set(Models.ChangeState.None, Models.ChangeState.Renamed);
break;
case " C":
change.Set(Models.ChangeState.None, Models.ChangeState.Copied);
break;
case "M":
change.Set(Models.ChangeState.Modified);
break;
case "MM":
change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified);
break;
case "MT":
change.Set(Models.ChangeState.Modified, Models.ChangeState.TypeChanged);
break;
case "MD":
change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted);
break;
case "T":
change.Set(Models.ChangeState.TypeChanged);
break;
case "TM":
change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Modified);
break;
case "TT":
change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.TypeChanged);
break;
case "TD":
change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Deleted);
break;
case "A":
change.Set(Models.ChangeState.Added);
break;
case "AM":
change.Set(Models.ChangeState.Added, Models.ChangeState.Modified);
break;
case "AT":
change.Set(Models.ChangeState.Added, Models.ChangeState.TypeChanged);
break;
case "AD":
change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted);
break;
case "D":
change.Set(Models.ChangeState.Deleted);
break;
case "R":
change.Set(Models.ChangeState.Renamed);
break;
case "RM":
change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified);
break;
case "RT":
change.Set(Models.ChangeState.Renamed, Models.ChangeState.TypeChanged);
break;
case "RD":
change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted);
break;
case "C":
change.Set(Models.ChangeState.Copied);
break;
case "CM":
change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified);
break;
case "CT":
change.Set(Models.ChangeState.Copied, Models.ChangeState.TypeChanged);
break;
case "CD":
change.Set(Models.ChangeState.Copied, Models.ChangeState.Deleted);
break;
case "DR":
change.Set(Models.ChangeState.Deleted, Models.ChangeState.Renamed);
break;
case "DC":
change.Set(Models.ChangeState.Deleted, Models.ChangeState.Copied);
break;
case "DD":
change.Set(Models.ChangeState.Deleted, Models.ChangeState.Deleted);
break;
case "AU":
change.Set(Models.ChangeState.Added, Models.ChangeState.Unmerged);
break;
case "UD":
change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Deleted);
break;
case "UA":
change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Added);
break;
case "DU":
change.Set(Models.ChangeState.Deleted, Models.ChangeState.Unmerged);
break;
case "AA":
change.Set(Models.ChangeState.Added, Models.ChangeState.Added);
break;
case "UU":
change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Unmerged);
break;
case "??":
change.Set(Models.ChangeState.Untracked, Models.ChangeState.Untracked);
break;
default:
return;
Dispatcher.UIThread.Post(() => App.RaiseException(Context, rs.StdErr));
return outs;
}
_changes.Add(change);
}
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_FORMAT().Match(line);
if (!match.Success)
continue;
private readonly List<Models.Change> _changes = new List<Models.Change>();
var change = new Models.Change() { Path = match.Groups[2].Value };
var status = match.Groups[1].Value;
switch (status)
{
case " M":
change.Set(Models.ChangeState.None, Models.ChangeState.Modified);
break;
case " T":
change.Set(Models.ChangeState.None, Models.ChangeState.TypeChanged);
break;
case " A":
change.Set(Models.ChangeState.None, Models.ChangeState.Added);
break;
case " D":
change.Set(Models.ChangeState.None, Models.ChangeState.Deleted);
break;
case " R":
change.Set(Models.ChangeState.None, Models.ChangeState.Renamed);
break;
case " C":
change.Set(Models.ChangeState.None, Models.ChangeState.Copied);
break;
case "M":
change.Set(Models.ChangeState.Modified);
break;
case "MM":
change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified);
break;
case "MT":
change.Set(Models.ChangeState.Modified, Models.ChangeState.TypeChanged);
break;
case "MD":
change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted);
break;
case "T":
change.Set(Models.ChangeState.TypeChanged);
break;
case "TM":
change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Modified);
break;
case "TT":
change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.TypeChanged);
break;
case "TD":
change.Set(Models.ChangeState.TypeChanged, Models.ChangeState.Deleted);
break;
case "A":
change.Set(Models.ChangeState.Added);
break;
case "AM":
change.Set(Models.ChangeState.Added, Models.ChangeState.Modified);
break;
case "AT":
change.Set(Models.ChangeState.Added, Models.ChangeState.TypeChanged);
break;
case "AD":
change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted);
break;
case "D":
change.Set(Models.ChangeState.Deleted);
break;
case "R":
change.Set(Models.ChangeState.Renamed);
break;
case "RM":
change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified);
break;
case "RT":
change.Set(Models.ChangeState.Renamed, Models.ChangeState.TypeChanged);
break;
case "RD":
change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted);
break;
case "C":
change.Set(Models.ChangeState.Copied);
break;
case "CM":
change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified);
break;
case "CT":
change.Set(Models.ChangeState.Copied, Models.ChangeState.TypeChanged);
break;
case "CD":
change.Set(Models.ChangeState.Copied, Models.ChangeState.Deleted);
break;
case "DD":
change.ConflictReason = Models.ConflictReason.BothDeleted;
change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
break;
case "AU":
change.ConflictReason = Models.ConflictReason.AddedByUs;
change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
break;
case "UD":
change.ConflictReason = Models.ConflictReason.DeletedByThem;
change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
break;
case "UA":
change.ConflictReason = Models.ConflictReason.AddedByThem;
change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
break;
case "DU":
change.ConflictReason = Models.ConflictReason.DeletedByUs;
change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
break;
case "AA":
change.ConflictReason = Models.ConflictReason.BothAdded;
change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
break;
case "UU":
change.ConflictReason = Models.ConflictReason.BothModified;
change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted);
break;
case "??":
change.Set(Models.ChangeState.None, Models.ChangeState.Untracked);
break;
}
if (change.Index != Models.ChangeState.None || change.WorkTree != Models.ChangeState.None)
outs.Add(change);
}
return outs;
}
}
}

View file

@ -20,7 +20,7 @@ namespace SourceGit.Commands
if (!output.IsSuccess)
return rs;
var lines = output.StdOut.Split('\n');
var lines = output.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line.EndsWith("/HEAD", StringComparison.Ordinal))

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
@ -17,27 +18,31 @@ namespace SourceGit.Commands
public List<Models.Remote> Result()
{
Exec();
return _loaded;
}
var outs = new List<Models.Remote>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return outs;
protected override void OnReadline(string line)
{
var match = REG_REMOTE().Match(line);
if (!match.Success)
return;
var remote = new Models.Remote()
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
Name = match.Groups[1].Value,
URL = match.Groups[2].Value,
};
var match = REG_REMOTE().Match(line);
if (!match.Success)
continue;
if (_loaded.Find(x => x.Name == remote.Name) != null)
return;
_loaded.Add(remote);
var remote = new Models.Remote()
{
Name = match.Groups[1].Value,
URL = match.Groups[2].Value,
};
if (outs.Find(x => x.Name == remote.Name) != null)
continue;
outs.Add(remote);
}
return outs;
}
private readonly List<Models.Remote> _loaded = new List<Models.Remote>();
}
}

View file

@ -0,0 +1,21 @@
namespace SourceGit.Commands
{
public class QueryRevisionByRefName : Command
{
public QueryRevisionByRefName(string repo, string refname)
{
WorkingDirectory = repo;
Context = repo;
Args = $"rev-parse {refname}";
}
public string Result()
{
var rs = ReadToEnd();
if (rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut))
return rs.StdOut.Trim();
return null;
}
}
}

View file

@ -1,4 +1,6 @@
namespace SourceGit.Commands
using System.Collections.Generic;
namespace SourceGit.Commands
{
public class QueryRevisionFileNames : Command
{
@ -9,13 +11,17 @@
Args = $"ls-tree -r -z --name-only {revision}";
}
public string[] Result()
public List<string> Result()
{
var rs = ReadToEnd();
if (rs.IsSuccess)
return rs.StdOut.Split('\0', System.StringSplitOptions.RemoveEmptyEntries);
if (!rs.IsSuccess)
return [];
return [];
var lines = rs.StdOut.Split('\0', System.StringSplitOptions.RemoveEmptyEntries);
var outs = new List<string>();
foreach (var line in lines)
outs.Add(line);
return outs;
}
}
}

View file

@ -8,7 +8,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = $"show --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s -s {sha}";
Args = $"show --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s -s {sha}";
}
public Models.Commit Result()

View file

@ -6,87 +6,87 @@ namespace SourceGit.Commands
{
public partial class QueryStagedChangesWithAmend : Command
{
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ACDMTUX])\d{0,6}\t(.*)$")]
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} ([ACDMT])\d{0,6}\t(.*)$")]
private static partial Regex REG_FORMAT1();
[GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} R\d{0,6}\t(.*\t.*)$")]
private static partial Regex REG_FORMAT2();
public QueryStagedChangesWithAmend(string repo)
public QueryStagedChangesWithAmend(string repo, string parent)
{
WorkingDirectory = repo;
Context = repo;
Args = "diff-index --cached -M HEAD^";
Args = $"diff-index --cached -M {parent}";
_parent = parent;
}
public List<Models.Change> Result()
{
var rs = ReadToEnd();
if (rs.IsSuccess)
if (!rs.IsSuccess)
return [];
var changes = new List<Models.Change>();
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var changes = new List<Models.Change>();
var lines = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
var match = REG_FORMAT2().Match(line);
if (match.Success)
{
var match = REG_FORMAT2().Match(line);
if (match.Success)
var change = new Models.Change()
{
var change = new Models.Change()
Path = match.Groups[3].Value,
DataForAmend = new Models.ChangeDataForAmend()
{
Path = match.Groups[3].Value,
DataForAmend = new Models.ChangeDataForAmend()
{
FileMode = match.Groups[1].Value,
ObjectHash = match.Groups[2].Value,
},
};
change.Set(Models.ChangeState.Renamed);
changes.Add(change);
continue;
}
match = REG_FORMAT1().Match(line);
if (match.Success)
{
var change = new Models.Change()
{
Path = match.Groups[4].Value,
DataForAmend = new Models.ChangeDataForAmend()
{
FileMode = match.Groups[1].Value,
ObjectHash = match.Groups[2].Value,
},
};
var type = match.Groups[3].Value;
switch (type)
{
case "A":
change.Set(Models.ChangeState.Added);
break;
case "C":
change.Set(Models.ChangeState.Copied);
break;
case "D":
change.Set(Models.ChangeState.Deleted);
break;
case "M":
change.Set(Models.ChangeState.Modified);
break;
case "T":
change.Set(Models.ChangeState.TypeChanged);
break;
case "U":
change.Set(Models.ChangeState.Unmerged);
break;
}
changes.Add(change);
}
FileMode = match.Groups[1].Value,
ObjectHash = match.Groups[2].Value,
ParentSHA = _parent,
},
};
change.Set(Models.ChangeState.Renamed);
changes.Add(change);
continue;
}
return changes;
match = REG_FORMAT1().Match(line);
if (match.Success)
{
var change = new Models.Change()
{
Path = match.Groups[4].Value,
DataForAmend = new Models.ChangeDataForAmend()
{
FileMode = match.Groups[1].Value,
ObjectHash = match.Groups[2].Value,
ParentSHA = _parent,
},
};
var type = match.Groups[3].Value;
switch (type)
{
case "A":
change.Set(Models.ChangeState.Added);
break;
case "C":
change.Set(Models.ChangeState.Copied);
break;
case "D":
change.Set(Models.ChangeState.Deleted);
break;
case "M":
change.Set(Models.ChangeState.Modified);
break;
case "T":
change.Set(Models.ChangeState.TypeChanged);
break;
}
changes.Add(change);
}
}
return [];
return changes;
}
private readonly string _parent;
}
}

View file

@ -9,52 +9,60 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = "stash list --pretty=format:%H%n%P%n%ct%n%gd%n%s";
Args = $"stash list -z --no-show-signature --format=\"%H%n%P%n%ct%n%gd%n%B\"";
}
public List<Models.Stash> Result()
{
Exec();
return _stashes;
}
var outs = new List<Models.Stash>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return outs;
protected override void OnReadline(string line)
{
switch (_nextLineIdx)
var items = rs.StdOut.Split('\0', StringSplitOptions.RemoveEmptyEntries);
foreach (var item in items)
{
case 0:
_current = new Models.Stash() { SHA = line };
_stashes.Add(_current);
break;
case 1:
ParseParent(line);
break;
case 2:
_current.Time = ulong.Parse(line);
break;
case 3:
_current.Name = line;
break;
case 4:
_current.Message = line;
break;
var current = new Models.Stash();
var nextPartIdx = 0;
var start = 0;
var end = item.IndexOf('\n', start);
while (end > 0 && nextPartIdx < 4)
{
var line = item.Substring(start, end - start);
switch (nextPartIdx)
{
case 0:
current.SHA = line;
break;
case 1:
if (line.Length > 6)
current.Parents.AddRange(line.Split(' ', StringSplitOptions.RemoveEmptyEntries));
break;
case 2:
current.Time = ulong.Parse(line);
break;
case 3:
current.Name = line;
break;
}
nextPartIdx++;
start = end + 1;
if (start >= item.Length - 1)
break;
end = item.IndexOf('\n', start);
}
if (start < item.Length)
current.Message = item.Substring(start);
outs.Add(current);
}
_nextLineIdx++;
if (_nextLineIdx > 4)
_nextLineIdx = 0;
return outs;
}
private void ParseParent(string data)
{
if (data.Length < 8)
return;
_current.Parents.AddRange(data.Split(separator: ' ', options: StringSplitOptions.RemoveEmptyEntries));
}
private readonly List<Models.Stash> _stashes = new List<Models.Stash>();
private Models.Stash _current = null;
private int _nextLineIdx = 0;
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
@ -6,12 +7,12 @@ namespace SourceGit.Commands
{
public partial class QuerySubmodules : Command
{
[GeneratedRegex(@"^[\-\+ ][0-9a-f]+\s(.*)\s\(.*\)$")]
private static partial Regex REG_FORMAT1();
[GeneratedRegex(@"^[\-\+ ][0-9a-f]+\s(.*)$")]
private static partial Regex REG_FORMAT2();
[GeneratedRegex(@"^\s?[\w\?]{1,4}\s+(.+)$")]
[GeneratedRegex(@"^([U\-\+ ])([0-9a-f]+)\s(.*?)(\s\(.*\))?$")]
private static partial Regex REG_FORMAT_STATUS();
[GeneratedRegex(@"^\s?[\w\?]{1,4}\s+(.+)$")]
private static partial Regex REG_FORMAT_DIRTY();
[GeneratedRegex(@"^submodule\.(\S*)\.(\w+)=(.*)$")]
private static partial Regex REG_FORMAT_MODULE_INFO();
public QuerySubmodules(string repo)
{
@ -25,52 +26,117 @@ namespace SourceGit.Commands
var submodules = new List<Models.Submodule>();
var rs = ReadToEnd();
var builder = new StringBuilder();
var lines = rs.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries);
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
var map = new Dictionary<string, Models.Submodule>();
var needCheckLocalChanges = false;
foreach (var line in lines)
{
var match = REG_FORMAT1().Match(line);
var match = REG_FORMAT_STATUS().Match(line);
if (match.Success)
{
var path = match.Groups[1].Value;
builder.Append($"\"{path}\" ");
submodules.Add(new Models.Submodule() { Path = path });
continue;
}
var stat = match.Groups[1].Value;
var sha = match.Groups[2].Value;
var path = match.Groups[3].Value;
match = REG_FORMAT2().Match(line);
if (match.Success)
{
var path = match.Groups[1].Value;
builder.Append($"\"{path}\" ");
submodules.Add(new Models.Submodule() { Path = path });
var module = new Models.Submodule() { Path = path, SHA = sha };
switch (stat[0])
{
case '-':
module.Status = Models.SubmoduleStatus.NotInited;
break;
case '+':
module.Status = Models.SubmoduleStatus.RevisionChanged;
break;
case 'U':
module.Status = Models.SubmoduleStatus.Unmerged;
break;
default:
module.Status = Models.SubmoduleStatus.Normal;
needCheckLocalChanges = true;
break;
}
map.Add(path, module);
submodules.Add(module);
}
}
if (submodules.Count > 0)
{
Args = $"status -uno --porcelain -- {builder}";
Args = "config --file .gitmodules --list";
rs = ReadToEnd();
if (rs.IsSuccess)
{
var modules = new Dictionary<string, ModuleInfo>();
lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_FORMAT_MODULE_INFO().Match(line);
if (match.Success)
{
var name = match.Groups[1].Value;
var key = match.Groups[2].Value;
var val = match.Groups[3].Value;
if (!modules.TryGetValue(name, out var m))
{
m = new ModuleInfo();
modules.Add(name, m);
}
if (key.Equals("path", StringComparison.Ordinal))
m.Path = val;
else if (key.Equals("url", StringComparison.Ordinal))
m.URL = val;
}
}
foreach (var kv in modules)
{
if (map.TryGetValue(kv.Value.Path, out var m))
m.URL = kv.Value.URL;
}
}
}
if (needCheckLocalChanges)
{
var builder = new StringBuilder();
foreach (var kv in map)
{
if (kv.Value.Status == Models.SubmoduleStatus.Normal)
{
builder.Append('"');
builder.Append(kv.Key);
builder.Append("\" ");
}
}
Args = $"--no-optional-locks status --porcelain -- {builder}";
rs = ReadToEnd();
if (!rs.IsSuccess)
return submodules;
var dirty = new HashSet<string>();
lines = rs.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries);
lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_FORMAT_STATUS().Match(line);
var match = REG_FORMAT_DIRTY().Match(line);
if (match.Success)
{
var path = match.Groups[1].Value;
dirty.Add(path);
if (map.TryGetValue(path, out var m))
m.Status = Models.SubmoduleStatus.Modified;
}
}
foreach (var submodule in submodules)
submodule.IsDirty = dirty.Contains(submodule.Path);
}
return submodules;
}
private class ModuleInfo
{
public string Path { get; set; } = string.Empty;
public string URL { get; set; } = string.Empty;
}
}
}

View file

@ -11,7 +11,7 @@ namespace SourceGit.Commands
Context = repo;
WorkingDirectory = repo;
Args = $"tag -l --format=\"{_boundary}%(refname)%00%(objectname)%00%(*objectname)%00%(creatordate:unix)%00%(contents:subject)%0a%0a%(contents:body)\"";
Args = $"tag -l --format=\"{_boundary}%(refname)%00%(objecttype)%00%(objectname)%00%(*objectname)%00%(creatordate:unix)%00%(contents:subject)%0a%0a%(contents:body)\"";
}
public List<Models.Tag> Result()
@ -24,17 +24,22 @@ namespace SourceGit.Commands
var records = rs.StdOut.Split(_boundary, StringSplitOptions.RemoveEmptyEntries);
foreach (var record in records)
{
var subs = record.Split('\0', StringSplitOptions.None);
if (subs.Length != 5)
var subs = record.Split('\0');
if (subs.Length != 6)
continue;
var message = subs[4].Trim();
var name = subs[0].Substring(10);
var message = subs[5].Trim();
if (!string.IsNullOrEmpty(message) && message.Equals(name, StringComparison.Ordinal))
message = null;
tags.Add(new Models.Tag()
{
Name = subs[0].Substring(10),
SHA = string.IsNullOrEmpty(subs[2]) ? subs[1] : subs[2],
CreatorDate = ulong.Parse(subs[3]),
Message = string.IsNullOrEmpty(message) ? null : message,
Name = name,
IsAnnotated = subs[1].Equals("tag", StringComparison.Ordinal),
SHA = string.IsNullOrEmpty(subs[3]) ? subs[2] : subs[3],
CreatorDate = ulong.Parse(subs[4]),
Message = message,
});
}

View file

@ -19,7 +19,7 @@ namespace SourceGit.Commands
if (!rs.IsSuccess)
return status;
var lines = rs.StdOut.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line[0] == '>')

View file

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
{
public partial class QueryUpdatableSubmodules : Command
{
[GeneratedRegex(@"^([U\-\+ ])([0-9a-f]+)\s(.*?)(\s\(.*\))?$")]
private static partial Regex REG_FORMAT_STATUS();
public QueryUpdatableSubmodules(string repo)
{
WorkingDirectory = repo;
Context = repo;
Args = "submodule status";
}
public List<string> Result()
{
var submodules = new List<string>();
var rs = ReadToEnd();
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_FORMAT_STATUS().Match(line);
if (match.Success)
{
var stat = match.Groups[1].Value;
var path = match.Groups[3].Value;
if (!stat.StartsWith(' '))
submodules.Add(path);
}
}
return submodules;
}
}
}

View file

@ -1,33 +1,7 @@
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Reset : Command
{
public Reset(string repo)
{
WorkingDirectory = repo;
Context = repo;
Args = "reset";
}
public Reset(string repo, List<Models.Change> changes)
{
WorkingDirectory = repo;
Context = repo;
var builder = new StringBuilder();
builder.Append("reset --");
foreach (var c in changes)
{
builder.Append(" \"");
builder.Append(c.Path);
builder.Append("\"");
}
Args = builder.ToString();
}
public Reset(string repo, string revision, string mode)
{
WorkingDirectory = repo;

View file

@ -1,29 +1,52 @@
using System.Collections.Generic;
using System.Text;
using System.Text;
namespace SourceGit.Commands
{
public class Restore : Command
{
public Restore(string repo)
/// <summary>
/// Only used for single staged change.
/// </summary>
/// <param name="repo"></param>
/// <param name="stagedChange"></param>
public Restore(string repo, Models.Change stagedChange)
{
WorkingDirectory = repo;
Context = repo;
Args = "restore . --source=HEAD --staged --worktree --recurse-submodules";
var builder = new StringBuilder();
builder.Append("restore --staged -- \"");
builder.Append(stagedChange.Path);
builder.Append('"');
if (stagedChange.Index == Models.ChangeState.Renamed)
{
builder.Append(" \"");
builder.Append(stagedChange.OriginalPath);
builder.Append('"');
}
Args = builder.ToString();
}
public Restore(string repo, List<string> files, string extra)
/// <summary>
/// Restore changes given in a path-spec file.
/// </summary>
/// <param name="repo"></param>
/// <param name="pathspecFile"></param>
/// <param name="isStaged"></param>
public Restore(string repo, string pathspecFile, bool isStaged)
{
WorkingDirectory = repo;
Context = repo;
StringBuilder builder = new StringBuilder();
var builder = new StringBuilder();
builder.Append("restore ");
if (!string.IsNullOrEmpty(extra))
builder.Append(extra).Append(" ");
builder.Append("--");
foreach (var f in files)
builder.Append(' ').Append('"').Append(f).Append('"');
builder.Append(isStaged ? "--staged " : "--worktree --recurse-submodules ");
builder.Append("--pathspec-from-file=\"");
builder.Append(pathspecFile);
builder.Append('"');
Args = builder.ToString();
}
}

View file

@ -10,15 +10,15 @@ namespace SourceGit.Commands
{
public static void Run(string repo, string revision, string file, string saveTo)
{
var dir = Path.GetDirectoryName(saveTo);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
var isLFSFiltered = new IsLFSFiltered(repo, revision, file).Result();
if (isLFSFiltered)
{
var tmpFile = saveTo + ".tmp";
if (ExecCmd(repo, $"show {revision}:\"{file}\"", tmpFile))
{
ExecCmd(repo, $"lfs smudge", saveTo, tmpFile);
}
File.Delete(tmpFile);
var pointerStream = QueryFileContent.Run(repo, revision, file);
ExecCmd(repo, $"lfs smudge", saveTo, pointerStream);
}
else
{
@ -26,7 +26,7 @@ namespace SourceGit.Commands
}
}
private static bool ExecCmd(string repo, string args, string outputFile, string inputFile = null)
private static void ExecCmd(string repo, string args, string outputFile, Stream input = null)
{
var starter = new ProcessStartInfo();
starter.WorkingDirectory = repo;
@ -45,27 +45,11 @@ namespace SourceGit.Commands
{
var proc = new Process() { StartInfo = starter };
proc.Start();
if (inputFile != null)
{
using (StreamReader sr = new StreamReader(inputFile))
{
while (true)
{
var line = sr.ReadLine();
if (line == null)
break;
proc.StandardInput.WriteLine(line);
}
}
}
if (input != null)
proc.StandardInput.Write(new StreamReader(input).ReadToEnd());
proc.StandardOutput.BaseStream.CopyTo(sw);
proc.WaitForExit();
var rs = proc.ExitCode == 0;
proc.Close();
return rs;
}
catch (Exception e)
{
@ -73,7 +57,6 @@ namespace SourceGit.Commands
{
App.RaiseException(repo, "Save file failed: " + e.Message);
});
return false;
}
}
}

View file

@ -82,7 +82,7 @@ namespace SourceGit.Commands
public bool Pop(string name)
{
Args = $"stash pop -q \"{name}\"";
Args = $"stash pop -q --index \"{name}\"";
return Exec();
}

View file

@ -1,4 +1,4 @@
using System;
using System;
namespace SourceGit.Commands
{
@ -8,7 +8,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = $"log --date-order --branches --remotes -{max} --pretty=format:\"%ct$%aN\"";
Args = $"log --date-order --branches --remotes -{max} --format=%ct$%aN±%aE";
}
public Models.Statistics Result()
@ -40,7 +40,7 @@ namespace SourceGit.Commands
if (dateEndIdx == -1)
return;
var dateStr = line.Substring(0, dateEndIdx);
var dateStr = line.AsSpan(0, dateEndIdx);
if (double.TryParse(dateStr, out var date))
statistics.AddCommit(line.Substring(dateEndIdx + 1), date);
}

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
{
@ -10,10 +11,9 @@ namespace SourceGit.Commands
Context = repo;
}
public bool Add(string url, string relativePath, bool recursive, Action<string> outputHandler)
public bool Add(string url, string relativePath, bool recursive)
{
_outputHandler = outputHandler;
Args = $"submodule add {url} \"{relativePath}\"";
Args = $"-c protocol.file.allow=always submodule add \"{url}\" \"{relativePath}\"";
if (!Exec())
return false;
@ -29,38 +29,38 @@ namespace SourceGit.Commands
}
}
public bool Update(string module, bool init, bool recursive, bool useRemote, Action<string> outputHandler)
public bool Update(List<string> modules, bool init, bool recursive, bool useRemote = false)
{
Args = "submodule update";
var builder = new StringBuilder();
builder.Append("submodule update");
if (init)
Args += " --init";
builder.Append(" --init");
if (recursive)
Args += " --recursive";
builder.Append(" --recursive");
if (useRemote)
Args += " --remote";
if (!string.IsNullOrEmpty(module))
Args += $" -- \"{module}\"";
builder.Append(" --remote");
if (modules.Count > 0)
{
builder.Append(" --");
foreach (var module in modules)
builder.Append($" \"{module}\"");
}
_outputHandler = outputHandler;
Args = builder.ToString();
return Exec();
}
public bool Delete(string relativePath)
public bool Deinit(string module, bool force)
{
Args = $"submodule deinit -f \"{relativePath}\"";
if (!Exec())
return false;
Args = $"rm -rf \"{relativePath}\"";
Args = force ? $"submodule deinit -f -- \"{module}\"" : $"submodule deinit -- \"{module}\"";
return Exec();
}
protected override void OnReadline(string line)
public bool Delete(string module)
{
_outputHandler?.Invoke(line);
Args = $"rm -rf \"{module}\"";
return Exec();
}
private Action<string> _outputHandler;
}
}

View file

@ -1,59 +1,51 @@
using System.Collections.Generic;
using System.IO;
using System.IO;
namespace SourceGit.Commands
{
public static class Tag
{
public static bool Add(string repo, string name, string basedOn)
public static bool Add(string repo, string name, string basedOn, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag {name} {basedOn}";
cmd.Args = $"tag --no-sign {name} {basedOn}";
cmd.Log = log;
return cmd.Exec();
}
public static bool Add(string repo, string name, string basedOn, string message, bool sign)
public static bool Add(string repo, string name, string basedOn, string message, bool sign, Models.ICommandLog log)
{
var param = sign ? "--sign -a" : "--no-sign -a";
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag {param} {name} {basedOn} ";
cmd.Log = log;
if (!string.IsNullOrEmpty(message))
{
string tmp = Path.GetTempFileName();
File.WriteAllText(tmp, message);
cmd.Args += $"-F \"{tmp}\"";
}
else
{
cmd.Args += $"-m {name}";
var succ = cmd.Exec();
File.Delete(tmp);
return succ;
}
cmd.Args += $"-m {name}";
return cmd.Exec();
}
public static bool Delete(string repo, string name, List<Models.Remote> remotes)
public static bool Delete(string repo, string name, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag --delete {name}";
if (!cmd.Exec())
return false;
if (remotes != null)
{
foreach (var r in remotes)
{
new Push(repo, r.Name, name, true).Exec();
}
}
return true;
cmd.Log = log;
return cmd.Exec();
}
}
}

View file

@ -23,13 +23,11 @@ namespace SourceGit.Commands
_patchBuilder.Append(c.DataForAmend.ObjectHash);
_patchBuilder.Append("\t");
_patchBuilder.Append(c.OriginalPath);
_patchBuilder.Append("\n");
}
else if (c.Index == Models.ChangeState.Added)
{
_patchBuilder.Append("0 0000000000000000000000000000000000000000\t");
_patchBuilder.Append(c.Path);
_patchBuilder.Append("\n");
}
else if (c.Index == Models.ChangeState.Deleted)
{
@ -37,7 +35,6 @@ namespace SourceGit.Commands
_patchBuilder.Append(c.DataForAmend.ObjectHash);
_patchBuilder.Append("\t");
_patchBuilder.Append(c.Path);
_patchBuilder.Append("\n");
}
else
{
@ -46,8 +43,9 @@ namespace SourceGit.Commands
_patchBuilder.Append(c.DataForAmend.ObjectHash);
_patchBuilder.Append("\t");
_patchBuilder.Append(c.Path);
_patchBuilder.Append("\n");
}
_patchBuilder.Append("\n");
}
}

View file

@ -1,23 +0,0 @@
using System;
namespace SourceGit.Commands
{
public class UpdateRef : Command
{
public UpdateRef(string repo, string refName, string toRevision, Action<string> outputHandler)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
Args = $"update-ref {refName} {toRevision}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private Action<string> _outputHandler;
}
}

View file

@ -21,7 +21,7 @@ namespace SourceGit.Commands
var last = null as Models.Worktree;
if (rs.IsSuccess)
{
var lines = rs.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line.StartsWith("worktree ", StringComparison.Ordinal))
@ -56,7 +56,7 @@ namespace SourceGit.Commands
return worktrees;
}
public bool Add(string fullpath, string name, bool createNew, string tracking, Action<string> outputHandler)
public bool Add(string fullpath, string name, bool createNew, string tracking)
{
Args = "worktree add ";
@ -78,14 +78,12 @@ namespace SourceGit.Commands
else if (!string.IsNullOrEmpty(name) && !createNew)
Args += name;
_outputHandler = outputHandler;
return Exec();
}
public bool Prune(Action<string> outputHandler)
public bool Prune()
{
Args = "worktree prune -v";
_outputHandler = outputHandler;
return Exec();
}
@ -101,22 +99,14 @@ namespace SourceGit.Commands
return Exec();
}
public bool Remove(string fullpath, bool force, Action<string> outputHandler)
public bool Remove(string fullpath, bool force)
{
if (force)
Args = $"worktree remove -f \"{fullpath}\"";
else
Args = $"worktree remove \"{fullpath}\"";
_outputHandler = outputHandler;
return Exec();
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private Action<string> _outputHandler = null;
}
}

View file

@ -7,8 +7,11 @@ namespace SourceGit.Converters
{
public static class ListConverters
{
public static readonly FuncValueConverter<IList, string> Count =
new FuncValueConverter<IList, string>(v => v == null ? "0" : $"{v.Count}");
public static readonly FuncValueConverter<IList, string> ToCount =
new FuncValueConverter<IList, string>(v => v == null ? " (0)" : $" ({v.Count})");
new FuncValueConverter<IList, string>(v => v == null ? "(0)" : $"({v.Count})");
public static readonly FuncValueConverter<IList, bool> IsNullOrEmpty =
new FuncValueConverter<IList, bool>(v => v == null || v.Count == 0);

View file

@ -0,0 +1,27 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace SourceGit.Converters
{
public static class ObjectConverters
{
public class IsTypeOfConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || parameter == null)
return false;
return value.GetType().IsAssignableTo((Type)parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return new NotImplementedException();
}
}
public static readonly IsTypeOfConverter IsTypeOf = new IsTypeOfConverter();
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using Avalonia.Data.Converters;
@ -22,7 +22,7 @@ namespace SourceGit.Converters
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length;
if (v.StartsWith(home, StringComparison.Ordinal))
return "~" + v.Substring(prefixLen);
return $"~{v.AsSpan(prefixLen)}";
return v;
});

View file

@ -81,5 +81,8 @@ namespace SourceGit.Converters
public static readonly FuncValueConverter<string, bool> ContainsSpaces =
new FuncValueConverter<string, bool>(v => v != null && v.Contains(' '));
public static readonly FuncValueConverter<string, bool> IsNotNullOrWhitespace =
new FuncValueConverter<string, bool>(v => v != null && v.Trim().Length > 0);
}
}

View file

@ -2,14 +2,22 @@
{
public class ApplyWhiteSpaceMode
{
public static readonly ApplyWhiteSpaceMode[] Supported =
[
new ApplyWhiteSpaceMode("No Warn", "Turns off the trailing whitespace warning", "nowarn"),
new ApplyWhiteSpaceMode("Warn", "Outputs warnings for a few such errors, but applies", "warn"),
new ApplyWhiteSpaceMode("Error", "Raise errors and refuses to apply the patch", "error"),
new ApplyWhiteSpaceMode("Error All", "Similar to 'error', but shows more", "error-all"),
];
public string Name { get; set; }
public string Desc { get; set; }
public string Arg { get; set; }
public ApplyWhiteSpaceMode(string n, string d, string a)
{
Name = App.Text(n);
Desc = App.Text(d);
Name = n;
Desc = d;
Arg = a;
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@ -17,7 +17,7 @@ namespace SourceGit.Models
{
public interface IAvatarHost
{
void OnAvatarResourceChanged(string email);
void OnAvatarResourceChanged(string email, Bitmap image);
}
public partial class AvatarManager
@ -26,10 +26,7 @@ namespace SourceGit.Models
{
get
{
if (_instance == null)
_instance = new AvatarManager();
return _instance;
return _instance ??= new AvatarManager();
}
}
@ -38,7 +35,7 @@ namespace SourceGit.Models
[GeneratedRegex(@"^(?:(\d+)\+)?(.+?)@.+\.github\.com$")]
private static partial Regex REG_GITHUB_USER_EMAIL();
private object _synclock = new object();
private readonly Lock _synclock = new();
private string _storePath;
private List<IAvatarHost> _avatars = new List<IAvatarHost>();
private Dictionary<string, Bitmap> _resources = new Dictionary<string, Bitmap>();
@ -119,7 +116,7 @@ namespace SourceGit.Models
Dispatcher.UIThread.InvokeAsync(() =>
{
_resources[email] = img;
NotifyResourceChanged(email);
NotifyResourceChanged(email, img);
});
}
@ -144,14 +141,13 @@ namespace SourceGit.Models
if (_defaultAvatars.Contains(email))
return null;
if (_resources.ContainsKey(email))
_resources.Remove(email);
_resources.Remove(email);
var localFile = Path.Combine(_storePath, GetEmailHash(email));
if (File.Exists(localFile))
File.Delete(localFile);
NotifyResourceChanged(email);
NotifyResourceChanged(email, null);
}
else
{
@ -179,13 +175,40 @@ namespace SourceGit.Models
lock (_synclock)
{
if (!_requesting.Contains(email))
_requesting.Add(email);
_requesting.Add(email);
}
return null;
}
public void SetFromLocal(string email, string file)
{
try
{
Bitmap image = null;
using (var stream = File.OpenRead(file))
{
image = Bitmap.DecodeToWidth(stream, 128);
}
if (image == null)
return;
_resources[email] = image;
_requesting.Remove(email);
var store = Path.Combine(_storePath, GetEmailHash(email));
File.Copy(file, store, true);
NotifyResourceChanged(email, image);
}
catch
{
// ignore
}
}
private void LoadDefaultAvatar(string key, string img)
{
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/{img}", UriKind.RelativeOrAbsolute));
@ -196,19 +219,17 @@ namespace SourceGit.Models
private string GetEmailHash(string email)
{
var lowered = email.ToLower(CultureInfo.CurrentCulture).Trim();
var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(lowered));
var builder = new StringBuilder();
var hash = MD5.HashData(Encoding.Default.GetBytes(lowered));
var builder = new StringBuilder(hash.Length * 2);
foreach (var c in hash)
builder.Append(c.ToString("x2"));
return builder.ToString();
}
private void NotifyResourceChanged(string email)
private void NotifyResourceChanged(string email, Bitmap image)
{
foreach (var avatar in _avatars)
{
avatar.OnAvatarResourceChanged(email);
}
avatar.OnAvatarResourceChanged(email, image);
}
}
}

35
src/Models/Bisect.cs Normal file
View file

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
namespace SourceGit.Models
{
public enum BisectState
{
None = 0,
WaitingForRange,
Detecting,
}
[Flags]
public enum BisectCommitFlag
{
None = 0,
Good = 1 << 0,
Bad = 1 << 1,
}
public class Bisect
{
public HashSet<string> Bads
{
get;
set;
} = [];
public HashSet<string> Goods
{
get;
set;
} = [];
}
}

View file

@ -23,10 +23,17 @@ namespace SourceGit.Models
}
}
public enum BranchSortMode
{
Name = 0,
CommitterDate,
}
public class Branch
{
public string Name { get; set; }
public string FullName { get; set; }
public ulong CommitterDate { get; set; }
public string Head { get; set; }
public bool IsLocal { get; set; }
public bool IsCurrent { get; set; }
@ -34,7 +41,7 @@ namespace SourceGit.Models
public string Upstream { get; set; }
public BranchTrackStatus TrackStatus { get; set; }
public string Remote { get; set; }
public bool IsUpsteamGone { get; set; }
public bool IsUpstreamGone { get; set; }
public string FriendlyName => IsLocal ? Name : $"{Remote}/{Name}";
}

View file

@ -18,14 +18,27 @@ namespace SourceGit.Models
Deleted,
Renamed,
Copied,
Unmerged,
Untracked
Untracked,
Conflicted,
}
public enum ConflictReason
{
None,
BothDeleted,
AddedByUs,
DeletedByThem,
AddedByThem,
DeletedByUs,
BothAdded,
BothModified,
}
public class ChangeDataForAmend
{
public string FileMode { get; set; } = "";
public string ObjectHash { get; set; } = "";
public string ParentSHA { get; set; } = "";
}
public class Change
@ -35,20 +48,14 @@ namespace SourceGit.Models
public string Path { get; set; } = "";
public string OriginalPath { get; set; } = "";
public ChangeDataForAmend DataForAmend { get; set; } = null;
public ConflictReason ConflictReason { get; set; } = ConflictReason.None;
public bool IsConflit
{
get
{
if (Index == ChangeState.Unmerged || WorkTree == ChangeState.Unmerged)
return true;
if (Index == ChangeState.Added && WorkTree == ChangeState.Added)
return true;
if (Index == ChangeState.Deleted && WorkTree == ChangeState.Deleted)
return true;
return false;
}
}
public bool IsConflicted => WorkTree == ChangeState.Conflicted;
public string ConflictMarker => CONFLICT_MARKERS[(int)ConflictReason];
public string ConflictDesc => CONFLICT_DESCS[(int)ConflictReason];
public string WorkTreeDesc => TYPE_DESCS[(int)WorkTree];
public string IndexDesc => TYPE_DESCS[(int)Index];
public void Set(ChangeState index, ChangeState workTree = ChangeState.None)
{
@ -76,8 +83,44 @@ namespace SourceGit.Models
if (Path[0] == '"')
Path = Path.Substring(1, Path.Length - 2);
if (!string.IsNullOrEmpty(OriginalPath) && OriginalPath[0] == '"')
OriginalPath = OriginalPath.Substring(1, OriginalPath.Length - 2);
}
private static readonly string[] TYPE_DESCS =
[
"Unknown",
"Modified",
"Type Changed",
"Added",
"Deleted",
"Renamed",
"Copied",
"Untracked",
"Conflict"
];
private static readonly string[] CONFLICT_MARKERS =
[
string.Empty,
"DD",
"AU",
"UD",
"UA",
"DU",
"AA",
"UU"
];
private static readonly string[] CONFLICT_DESCS =
[
string.Empty,
"Both deleted",
"Added by us",
"Deleted by them",
"Added by them",
"Deleted by us",
"Both added",
"Both modified"
];
}
}

View file

@ -8,14 +8,19 @@ namespace SourceGit.Models
{
public enum CommitSearchMethod
{
BySHA = 0,
ByAuthor,
ByCommitter,
ByMessage,
ByFile,
ByContent,
}
public class Commit
{
// As retrieved by: git mktree </dev/null
public const string EmptyTreeSHA1 = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
public static double OpacityForNotMerged
{
get;
@ -28,13 +33,14 @@ namespace SourceGit.Models
public User Committer { get; set; } = User.Invalid;
public ulong CommitterTime { get; set; } = 0;
public string Subject { get; set; } = string.Empty;
public List<string> Parents { get; set; } = new List<string>();
public List<Decorator> Decorators { get; set; } = new List<Decorator>();
public List<string> Parents { get; set; } = new();
public List<Decorator> Decorators { get; set; } = new();
public bool HasDecorators => Decorators.Count > 0;
public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Actived.DateTime);
public string CommitterTimeStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Actived.DateTime);
public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Actived.DateOnly);
public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateTime);
public string CommitterTimeStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Active.DateTime);
public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly);
public string CommitterTimeShortStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly);
public bool IsMerged { get; set; } = false;
public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime;
@ -43,7 +49,7 @@ namespace SourceGit.Models
public int Color { get; set; } = 0;
public double Opacity => IsMerged ? 1 : OpacityForNotMerged;
public FontWeight FontWeight => IsCurrentHead ? FontWeight.Bold : FontWeight.Regular;
public Thickness Margin { get; set; } = new Thickness(0);
public Thickness Margin { get; set; } = new(0);
public IBrush Brush => CommitGraph.Pens[Color].Brush;
public void ParseDecorators(string data)
@ -107,14 +113,14 @@ namespace SourceGit.Models
if (l.Type != r.Type)
return (int)l.Type - (int)r.Type;
else
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
return NumericSort.Compare(l.Name, r.Name);
});
}
}
public class CommitWithMessage
public class CommitFullMessage
{
public Commit Commit { get; set; } = new Commit();
public string Message { get; set; } = "";
public string Message { get; set; } = string.Empty;
public InlineElementCollector Inlines { get; set; } = new();
}
}

View file

@ -64,8 +64,8 @@ namespace SourceGit.Models
{
const double unitWidth = 12;
const double halfWidth = 6;
const double unitHeight = 28;
const double halfHeight = 14;
const double unitHeight = 1;
const double halfHeight = 0.5;
var temp = new CommitGraph();
var unsolved = new List<PathHelper>();

View file

@ -1,8 +1,49 @@
namespace SourceGit.Models
using System;
using System.Collections.Generic;
namespace SourceGit.Models
{
public class CommitLink
{
public string Name { get; set; } = null;
public string URLPrefix { get; set; } = null;
public CommitLink(string name, string prefix)
{
Name = name;
URLPrefix = prefix;
}
public static List<CommitLink> Get(List<Remote> remotes)
{
var outs = new List<CommitLink>();
foreach (var remote in remotes)
{
if (remote.TryGetVisitURL(out var url))
{
var trimmedUrl = url.AsSpan();
if (url.EndsWith(".git"))
trimmedUrl = url.AsSpan(0, url.Length - 4);
if (url.StartsWith("https://github.com/", StringComparison.Ordinal))
outs.Add(new($"Github ({trimmedUrl.Slice(19)})", $"{url}/commit/"));
else if (url.StartsWith("https://gitlab.", StringComparison.Ordinal))
outs.Add(new($"GitLab ({trimmedUrl.Slice(trimmedUrl.Slice(15).IndexOf('/') + 16)})", $"{url}/-/commit/"));
else if (url.StartsWith("https://gitee.com/", StringComparison.Ordinal))
outs.Add(new($"Gitee ({trimmedUrl.Slice(18)})", $"{url}/commit/"));
else if (url.StartsWith("https://bitbucket.org/", StringComparison.Ordinal))
outs.Add(new($"BitBucket ({trimmedUrl.Slice(22)})", $"{url}/commits/"));
else if (url.StartsWith("https://codeberg.org/", StringComparison.Ordinal))
outs.Add(new($"Codeberg ({trimmedUrl.Slice(21)})", $"{url}/commit/"));
else if (url.StartsWith("https://gitea.org/", StringComparison.Ordinal))
outs.Add(new($"Gitea ({trimmedUrl.Slice(18)})", $"{url}/commit/"));
else if (url.StartsWith("https://git.sr.ht/", StringComparison.Ordinal))
outs.Add(new($"sourcehut ({trimmedUrl.Slice(18)})", $"{url}/commit/"));
}
}
return outs;
}
}
}

View file

@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.Models
{
public partial class CommitTemplate : ObservableObject
public class CommitTemplate : ObservableObject
{
public string Name
{

View file

@ -4,23 +4,28 @@ namespace SourceGit.Models
{
public class ConventionalCommitType
{
public string Type { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Name { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public static readonly List<ConventionalCommitType> Supported = new List<ConventionalCommitType>()
{
new ConventionalCommitType("feat", "Adding a new feature"),
new ConventionalCommitType("fix", "Fixing a bug"),
new ConventionalCommitType("docs", "Updating documentation"),
new ConventionalCommitType("style", "Elements or code styles without changing the code logic"),
new ConventionalCommitType("test", "Adding or updating tests"),
new ConventionalCommitType("chore", "Making changes to the build process or auxiliary tools and libraries"),
new ConventionalCommitType("revert", "Undoing a previous commit"),
new ConventionalCommitType("refactor", "Restructuring code without changing its external behavior")
};
public static readonly List<ConventionalCommitType> Supported = [
new("Features", "feat", "Adding a new feature"),
new("Bug Fixes", "fix", "Fixing a bug"),
new("Work In Progress", "wip", "Still being developed and not yet complete"),
new("Reverts", "revert", "Undoing a previous commit"),
new("Code Refactoring", "refactor", "Restructuring code without changing its external behavior"),
new("Performance Improvements", "perf", "Improves performance"),
new("Builds", "build", "Changes that affect the build system or external dependencies"),
new("Continuous Integrations", "ci", "Changes to CI configuration files and scripts"),
new("Documentations", "docs", "Updating documentation"),
new("Styles", "style", "Elements or code styles without changing the code logic"),
new("Tests", "test", "Adding or updating tests"),
new("Chores", "chore", "Other changes that don't modify src or test files"),
];
public ConventionalCommitType(string type, string description)
public ConventionalCommitType(string name, string type, string description)
{
Name = name;
Type = type;
Description = description;
}

19
src/Models/Count.cs Normal file
View file

@ -0,0 +1,19 @@
using System;
namespace SourceGit.Models
{
public class Count : IDisposable
{
public int Value { get; set; } = 0;
public Count(int value)
{
Value = value;
}
public void Dispose()
{
// Ignore
}
}
}

View file

@ -25,24 +25,24 @@ namespace SourceGit.Models
set;
} = 0;
public static DateTimeFormat Actived
public static DateTimeFormat Active
{
get => Supported[ActiveIndex];
}
public static readonly List<DateTimeFormat> Supported = new List<DateTimeFormat>
{
new DateTimeFormat("yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss"),
new DateTimeFormat("yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss"),
new DateTimeFormat("yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"),
new DateTimeFormat("MM/dd/yyyy", "MM/dd/yyyy HH:mm:ss"),
new DateTimeFormat("MM.dd.yyyy", "MM.dd.yyyy HH:mm:ss"),
new DateTimeFormat("MM-dd-yyyy", "MM-dd-yyyy HH:mm:ss"),
new DateTimeFormat("dd/MM/yyyy", "dd/MM/yyyy HH:mm:ss"),
new DateTimeFormat("dd.MM.yyyy", "dd.MM.yyyy HH:mm:ss"),
new DateTimeFormat("dd-MM-yyyy", "dd-MM-yyyy HH:mm:ss"),
new DateTimeFormat("MMM d yyyy", "MMM d yyyy HH:mm:ss"),
new DateTimeFormat("d MMM yyyy", "d MMM yyyy HH:mm:ss"),
new DateTimeFormat("yyyy/MM/dd", "yyyy/MM/dd, HH:mm:ss"),
new DateTimeFormat("yyyy.MM.dd", "yyyy.MM.dd, HH:mm:ss"),
new DateTimeFormat("yyyy-MM-dd", "yyyy-MM-dd, HH:mm:ss"),
new DateTimeFormat("MM/dd/yyyy", "MM/dd/yyyy, HH:mm:ss"),
new DateTimeFormat("MM.dd.yyyy", "MM.dd.yyyy, HH:mm:ss"),
new DateTimeFormat("MM-dd-yyyy", "MM-dd-yyyy, HH:mm:ss"),
new DateTimeFormat("dd/MM/yyyy", "dd/MM/yyyy, HH:mm:ss"),
new DateTimeFormat("dd.MM.yyyy", "dd.MM.yyyy, HH:mm:ss"),
new DateTimeFormat("dd-MM-yyyy", "dd-MM-yyyy, HH:mm:ss"),
new DateTimeFormat("MMM d yyyy", "MMM d yyyy, HH:mm:ss"),
new DateTimeFormat("d MMM yyyy", "d MMM yyyy, HH:mm:ss"),
};
private static readonly DateTime _example = new DateTime(2025, 1, 31, 8, 0, 0, DateTimeKind.Local);

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace SourceGit.Models
{
public class DealWithChangesAfterStashing
{
public string Label { get; set; }
public string Desc { get; set; }
public static readonly List<DealWithChangesAfterStashing> Supported = [
new ("Discard", "All (or selected) changes will be discarded"),
new ("Keep Index", "Staged changes are left intact"),
new ("Keep All", "All (or selected) changes are left intact"),
];
public DealWithChangesAfterStashing(string label, string desc)
{
Label = label;
Desc = desc;
}
}
}

Some files were not shown because too many files have changed in this diff Show more