aboutsummaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
* Merge "Remove use of deprecated java.security.AccessController"Matthias Sohn12 days1-9/+6
|\
| * Remove use of deprecated java.security.AccessControllerMatthias Sohn12 days1-9/+6
| | | | | | | | | | | | It's deprecated and marked for removal since Java 17. Change-Id: I6d0d4ac08f10cc73a409f202628a23faed4e5b36
* | Merge "PersonIdent: Default to UTC in timezone parsing"Ivan Frade12 days1-2/+9
|\ \ | |/ |/|
| * PersonIdent: Default to UTC in timezone parsingIvan Frade13 days1-2/+9
| | | | | | | | | | | | | | | | | | | | | | The old method "getTimeZone(int tzOffset)" defaults to UTC if the offset is out of range, but the new "getZoneId(int tzOffset)" throws an exception. Return UTC if the offset is invalid, to keep the behavior consistent with older code. Change-Id: Iffe1980b3bd9c05ef2293635a1cbb493144afb79
* | Merge "Update .mailmap"Matthias Sohn12 days1-0/+13
|\ \ | |/ |/|
| * Update .mailmapMatthias Sohn2024-12-081-0/+13
| | | | | | | | | | | | | | | | for contributors - who used multiple email addresses - who used multiple display names Change-Id: If868bfdfe6fbb37af2360e5553fb9453c7fa4835
* | RawParseUtils: Default to UTC in invalid timezonesIvan Frade13 days2-2/+23
|/ | | | | | | | | | | | | PersonIdent used to translate invalid timezones to UTC [1], but the new java.time code just throws an exception. Also the parsing used happen on demand, but now is done in the constructor, so the exception is thrown even if the timezone is not used at all. Check the parsed timezone and default to UTC if it is out of range. [1] https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getTimeZone-java.lang.String- Change-Id: I90dd7d842ac8f44caef3b76d57375dead76bebde
* Merge "RawParseUtils: Use java.time API to build PersonIdents"Ivan Frade2024-12-061-14/+39
|\
| * RawParseUtils: Use java.time API to build PersonIdentsIvan Frade2024-12-051-14/+39
| | | | | | | | | | | | | | | | | | | | PersonIdent has deprecated the long/int constructors for the time, in favor of Instant/ZoneId. Update RawParseUtils to create PersonIdents with the new constructors. Change-Id: Ic62ff0c4219b4bd228c694e73e00a7c794e3553a
* | Merge branch 'stable-7.1'Matthias Sohn2024-12-069-48/+87
|\ \ | |/ |/| | | | | | | | | | | | | | | | | | | | | | | | | * stable-7.1: FileSnapshot: silence "Not a Directory" exceptions FileSnapshot: refactor to share error handling Mark Attribute#getValue as @Nullable Fix potential NPE in ResolveMerger#getAttributesContentMergeStrategy Fix NPE in DiffFormatter#getDiffDriver Pack: ensure packfile is still valid while still recoverable WindowCache: add bulk purge(), call from bulk sites UploadPack#implies: add missing @since tag Disable MergeToolTest#testEmptyToolName Change-Id: Ifa8df2b9e6e4ee371113b7114fe20b42333e3718
| * Merge branch 'stable-7.0' into stable-7.1Matthias Sohn2024-12-0610-48/+95
| |\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * stable-7.0: FileSnapshot: silence "Not a Directory" exceptions FileSnapshot: refactor to share error handling Mark Attribute#getValue as @Nullable Fix potential NPE in ResolveMerger#getAttributesContentMergeStrategy Fix NPE in DiffFormatter#getDiffDriver Pack: ensure packfile is still valid while still recoverable WindowCache: add bulk purge(), call from bulk sites UploadPack#implies: add missing @since tag Disable MergeToolTest#testEmptyToolName Change-Id: Icb25fed5b703c6a39a64231fd8ca93c1f1a581be
| | * Merge branch 'stable-6.10' into stable-7.0Matthias Sohn2024-12-069-48/+89
| | |\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * stable-6.10: FileSnapshot: silence "Not a Directory" exceptions FileSnapshot: refactor to share error handling Mark Attribute#getValue as @Nullable Fix potential NPE in ResolveMerger#getAttributesContentMergeStrategy Fix NPE in DiffFormatter#getDiffDriver Pack: ensure packfile is still valid while still recoverable WindowCache: add bulk purge(), call from bulk sites UploadPack#implies: add missing @since tag Disable MergeToolTest#testEmptyToolName Change-Id: I854f44e76b73ae434a0d6b6ab782fd0aed72f219
| | | * Merge changes I022639c4,I1fcce5ef into stable-6.10Matthias Sohn2024-12-051-15/+20
| | | |\ | | | | | | | | | | | | | | | | | | | | | | | | | * changes: FileSnapshot: silence "Not a Directory" exceptions FileSnapshot: refactor to share error handling
| | | | * FileSnapshot: silence "Not a Directory" exceptionsMartin Fick2024-12-051-0/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Sometimes a FileSystemException with "Not a Directory" can be thrown while creating a FileSnapshot, likely because the file or directory was deleted. Since NoSuchFileExceptions are already silenced, and the FileSnapshot already handles all IOExceptions, there is likely no value in seeing this info in the logs, treat these situation the same and silence them also. Change-Id: I022639c42154a0a4c71065741f023f5eb95f9b55 Signed-off-by: Martin Fick <mfick@nvidia.com>
| | | | * FileSnapshot: refactor to share error handlingMartin Fick2024-12-051-15/+15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | It is important to keep the exception handling for getting file attributes the same in all places in this class; put that code into a common method. Change-Id: I1fcce5efd10aa562a4e0e34d3ce94bcc83850237 Signed-off-by: Martin Fick <mfick@nvidia.com>
| | | * | Merge changes I172c43ff,I2a4c5743,Icc4b8d2f into stable-6.10Matthias Sohn2024-12-053-10/+20
| | | |\ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * changes: Mark Attribute#getValue as @Nullable Fix potential NPE in ResolveMerger#getAttributesContentMergeStrategy Fix NPE in DiffFormatter#getDiffDriver
| | | | * | Mark Attribute#getValue as @NullableMatthias Sohn2024-12-051-0/+3
| | | | | | | | | | | | | | | | | | | | | | | | Change-Id: I172c43ff2e3e682f38a91ce288190245fa5d5ebe
| | | | * | Fix potential NPE in ResolveMerger#getAttributesContentMergeStrategyMatthias Sohn2024-12-051-3/+6
| | | | | | | | | | | | | | | | | | | | | | | | Change-Id: I2a4c57438c16a0c5bc1af4c7772eaf65049911e2
| | | | * | Fix NPE in DiffFormatter#getDiffDriverMatthias Sohn2024-12-051-7/+11
| | | | |/ | | | | | | | | | | | | | | | | | | | | | | | | | Guard against diff attribute with a null value. Change-Id: Icc4b8d2f360ef105c6d64716cb42f2979967075b
| | | * / Pack: ensure packfile is still valid while still recoverableMartin Fick2024-12-041-1/+7
| | | |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When copyAsIs2() needs to send a large object requiring multiple read iterations — any of which could fail if the object isn't already in the WindowCache — verify first that the packfile is still valid at the latest possible moment, just before sending the header that would make recovery impossible. Change-Id: I234fd4b315b579a0506c8fbdea0c6787bdc09fcd Signed-off-by: Martin Fick <mfick@nvidia.com>
| | | * WindowCache: add bulk purge(), call from bulk sitesMartin Fick2024-12-023-21/+37
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Purging a Pack from the WindowCache requires a linear search over all the entries in the cache, and thus is rather expensive. Since git gc often deletes more than one packfile at a time, avoid multiplying this expensive operation when possible by purging a Set of Packs when closing deleted pack files during a directory scan. Purge the Set of Packs with a single linear search of the cache. Closing the PackDirectory also cccbngljkihltghcnbiftcubdvgugdcvujkejehbjr Change-Id: Ic9b45cab57e1ef610c2e20ad392d8b45f8091f41 Signed-off-by: Martin Fick <mfick@nvidia.com>
| | | * UploadPack#implies: add missing @since tagMatthias Sohn2024-12-022-1/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | The method was originally introduced in 7.1 and then back ported to the stable-6.10 branch. Change-Id: I7250013227a666de44e439802d51110bba5af2b9
| | | * Disable MergeToolTest#testEmptyToolNameMatthias Sohn2024-12-021-0/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | which started to fail long time after it was merged. Disable the test to unblock other apparently unrelated changes until the root cause is found and fixed. Bug: jgit-115 Change-Id: Ic9a4c10a16feb7b61fb348b0b5a4b17961af0dfd
* | | | TreeWalk: Make a null check before dereferencing the config variable.jackdt@google.com2024-12-051-1/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Gerrit finds a null pointer exception accessing the conf in some merge operations. It is not clear why, but could be related to [1] and [2] where the attributes node provider is set (instead of being null). Check if the config is there before reading the value and return null if not (as if the config didn't have the value set). The config is @Nullable so this check makes sense in any case. [1] https://review.gerrithub.io/c/eclipse-jgit/jgit/+/1203278 [2] https://gerrit-review.git.corp.google.com/c/gerrit/+/441721 Change-Id: I2b786c0a23fa2e09fb9fe041c025d42823defb04
* | | | MergeToolTest: update expected error from the toolIvan Frade2024-12-041-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This test fails in jenkins becuase the error string is slightly different. It seems to come from an external tool (git mergetool?) that maybe changed that output (?). At the moment update the error string to match the output of the tool. Parsing error messages is bound to be flaky. Change-Id: I81e9bf65088b893af3a0d8e53d57052583fc2262
* | | | Update last jgit release version to 7.1.0.202411261347-rMatthias Sohn2024-11-271-1/+1
| | | | | | | | | | | | | | | | Change-Id: I7f392d5f334154437553141c0ab68917b56a7634
* | | | Remove unused API warning filtersMatthias Sohn2024-11-271-67/+0
| | | | | | | | | | | | | | | | Change-Id: I4ec17bb532cf1fb2634ef7f5fc93b624bad7f8f0
* | | | DiffFormatter: silence non-externalized string warningsMatthias Sohn2024-11-271-1/+1
| | | | | | | | | | | | | | | | Change-Id: I08488598d1cc58f17e6994cf00e74b20307ada06
* | | | Prepare 7.2.0-SNAPSHOT buildsMatthias Sohn2024-11-2789-540/+540
| | | | | | | | | | | | | | | | Change-Id: Ie3108fefbcbb55a4f26273833c9817ce4bd750f1
* | | | Merge branch 'stable-7.1'Matthias Sohn2024-11-2789-540/+540
|\| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * stable-7.1: Prepare 7.1.1-SNAPSHOT builds JGit v7.1.0.202411261347-r Prepare 7.1.0-SNAPSHOT builds JGit v7.1.0.202411191359-rc1 Change-Id: Iccfe871e7346f818a17bfc0b3fa4e312d8f1ee04
| * | | Prepare 7.1.1-SNAPSHOT buildsMatthias Sohn2024-11-2689-540/+540
| | | | | | | | | | | | | | | | Change-Id: Ifc710a83cda50f1275cbbd5a828f92d95607f298
| * | | JGit v7.1.0.202411261347-rv7.1.0.202411261347-rMatthias Sohn2024-11-2689-123/+123
| | | | | | | | | | | | | | | | | | | | Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Change-Id: I70f154dd1dc8571c5e2057d09d08d4a4d1b7ee37
| * | | Merge branch 'master' into stable-7.1Matthias Sohn2024-11-2624-260/+640
| |\ \ \ | |/ / / |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * master: PackDirectory: Filter out tmp GC pack files Add pack-refs command to the CLI Test advertised capabilities with protocol V0 and allow*Sha1InWant Align request policies with CGit GitTimeParser: Fix multiple errorprone and style comments PersonIdent: Preserve the timezone when copying with new time PersonIdent: Revert @since of #getZoneId tests/BasicTest: Use java.time constructors for PersonIdent RawParseUtils test: Use java.time to create PersonIdents Change default similarity score to 50(%) to match git's default Pack.java: Recover more often in Pack.copyAsIs2() Change-Id: I1386addb355ffba09dcbf1a6e8634d605c0d3b5c
* | | | Merge branch 'stable-7.0'Matthias Sohn2024-11-263-143/+162
|\ \ \ \ | | |/ / | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * stable-7.0: PackDirectory: Filter out tmp GC pack files Test advertised capabilities with protocol V0 and allow*Sha1InWant Align request policies with CGit Pack.java: Recover more often in Pack.copyAsIs2() Change-Id: Iddb994a747bc860dd9286e74190ecdd285ce6832
| * | | Merge branch 'stable-6.10' into stable-7.0Matthias Sohn2024-11-265-156/+229
| |\ \ \ | | | |/ | | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | * stable-6.10: PackDirectory: Filter out tmp GC pack files Test advertised capabilities with protocol V0 and allow*Sha1InWant Align request policies with CGit Pack.java: Recover more often in Pack.copyAsIs2() Change-Id: Ib301efa54aaf2196d764a0fc1f91f652b4d68396
| | * | Merge "Pack.java: Recover more often in Pack.copyAsIs2()" into stable-6.10Matthias Sohn2024-11-261-142/+153
| | |\ \
| | | * | Pack.java: Recover more often in Pack.copyAsIs2()Martin Fick2024-11-111-142/+153
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The PACK class is designed to throw StoredObjectRepresentationNotAvailableException at times when it cannot find an object which previously was believed to be in its packfile and it is still possible for the caller, PackWriter.writeObjectImpl(), to retry copying the object from another file and potentially avoid causing a user facing error for this fairly common expected situation. This retry helps handle when repacking causes a packfile to be replaced by new files with the same objects. Improve copyAsIs2() to drastically make recovery possible in more situations. Once any data for a specific object, has been sent it is very difficult to recover from that object being relocated to another pack. But if a read error is detected in copyAsIs2() before sending the object header (and thus any data), then it should still be recoverable. Fix three places where we could have recovered because we hadn't sent the header yet, and adjust another place to send the header a bit later, after having read some data from the object successfully. Basically, if the header has not been written yet, throw StoredObjectRepresentationNotAvailableException to signal that this is still recoverable. These fixes should drastically improve the chances of recovery since due to unix file semantics, if the partial read succeeds, then the full read will very likely succeed. This is because while the file may no longer be open when the first read is done (the WindowCache may have evicted it), once the first read completes it will likely still be open and even if the file is deleted the WindowCache will continue to be able to read from it until it closes it. Change-Id: Ib87e294e0dbacf71b10db55be511e91963b4a84a Signed-off-by: Martin Fick <mfick@nvidia.com>
| | * | | PackDirectory: Filter out tmp GC pack filesMartin Fick2024-11-222-1/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | git repack passes a ".tmp-XXXX-" prefix to git pack-objects when repacking. git pack-objects then adds a "pack-XXXXX.pack" to this to create the name of new packfiles it writes to. PackDirectory was previously very lenient and would allow these files to be added to its list of known packfiles. Fix PackDirectory to filter these out since they are not meant to be consumed yet, and doing so can cause user facing errors. Change-Id: I072e57d9522e02049db17d3f4977df7eda14bba7 Signed-off-by: Martin Fick <mfick@nvidia.com>
| | * | | Test advertised capabilities with protocol V0 and allow*Sha1InWantpszlazak2024-11-211-0/+42
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The advertised capabilities with protocol V0 were untested leading to potential regressions when advertising what SHA1 should or should not be on the list of capabilities. Verify that allow-tip-sha1-in-want and allow-reachable-sha1-in-want are properly advertised with the allow*Sha1InWant is set in jgit.config. Change-Id: Id48af2bc19280f2dcb26aa8e8765cde8f2ce7a06 (cherry picked from commit 5583f6a10eafc8c2627e0fb4833cb8ffe422f69a)
| | * | | Align request policies with CGitpszlazak2024-11-211-13/+25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | CGit defines the SHA request policies using a bitmask that represents which policy is implied by another policy. For example, in CGit the ALLOW_TIP_SHA1 is 0x01 and ALLOW_REACHABLE_SHA1 is 0x02, which are associated to two different bit in a 3-bit value. The ALLOW_ANY_SHA1 value is 0x07 which denotes a different policy that implies the previous two ones, because is represented with a 3-bit bitmask having all ones. Associate the JGit RequestPolicy enum to the same CGit bitmask values and use the same logic for the purpose of advertising the server capabilities. The JGit code becomes easier to read and associate with its counterpart in CGit, especially during the capabilities advertising phase. Also add a new utility method RequestPolicy.implies() which is more readable than a direct bitmask and operator. Bug: jgit-68 Change-Id: I932150dca1211ba9c8c34a523f13e84d7390063b (cherry picked from commit 1519c147948eb1108bdf45f2aeed84746dacff9c)
* | | | | Add pack-refs command to the CLIYash Chaturvedi2024-11-2214-54/+317
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This command can be used to optimize storage of references. For a RefDirectory database, it packs non-symbolic, loose refs into packed-refs. By default, only the references under '$GIT_DIR/refs/tags' are packed. The '--all' option can be used to pack all the references under '$GIT_DIR/refs'. For Reftable, all refs are compacted into a single table. Change-Id: I92e786403f8638d35ae3037844a7ad89e8959e02
* | | | | Merge "GitTimeParser: Fix multiple errorprone and style comments"Matthias Sohn2024-11-212-16/+27
|\ \ \ \ \
| * | | | | GitTimeParser: Fix multiple errorprone and style commentsIvan Frade2024-11-202-16/+27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This code came from GitDateParser and inherited these issues. It is a good time to fix them: * Use always {} in if and fors (eclipse guide) * Use the more efficient EnumMap for the formatters * variable.equals(CONSTANT) instead of CONSTANT.equals(var) https://errorprone.info/bugpattern/YodaCondition * private constructor to prevent instantiations of a utility classes https://errorprone.info/bugpattern/PrivateConstructorForUtilityClass * methods named in camelCase * Use string.split(..., int) https://errorprone.info/bugpattern/StringSplitter * Initialize variable in first use Change-Id: I0ae70ad9befc66fa9c057af135f2b0b2dab3869a
* | | | | | Merge branch 'stable-7.0'Matthias Sohn2024-11-2112-14/+829
|\ \ \ \ \ \ | |/ / / / / |/| / / / / | |/ / / / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * stable-7.0: DiffDriver: fix doc for rust built-in DiffDriver: fix formatting of javadoc Add numberOfObjectsSinceBitmap to RepoStatistics Support built-in diff drivers for hunk header function names Don't fail when trying to prune pack which is already gone Rename numberOfPackFilesAfterBitmap to numberOfPackFilesSinceBitmap Change-Id: I98beec1186132e749a749706f399237de7d7e45e
* | | | | PersonIdent: Preserve the timezone when copying with new timeIvan Frade2024-11-201-1/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The PersonIdent(PersonIdent,Date) constructor must create a copy with the same author/email/timezone but different time. When we changed the implementation to the new Instant/ZoneId version, we forgot to pass the timezone. This made fail some tests downstream. Pass the timezone when constructing the copy. Change-Id: Iaa979e6dbaa3c55d4c4d2040068ab8b03163cd4e
* | | | | PersonIdent: Revert @since of #getZoneIdIvan Frade2024-11-201-2/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In [1], the @since tag of #getZoneId was updated to 7.1 by mistake. The implementation of the method is different but the API hasn't changed. Revert the tag to 6.1, when the method was introduced. [1] https://gerrithub.io/c/eclipse-jgit/jgit/+/1204142/9/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java Change-Id: If71d763ac28d4ec02bfebb1e65f56227f44e027d
* | | | | Merge changes I3844bf1e,I05c759bdMatthias Sohn2024-11-202-43/+63
|\ \ \ \ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * changes: tests/BasicTest: Use java.time constructors for PersonIdent RawParseUtils test: Use java.time to create PersonIdents
| * | | | | tests/BasicTest: Use java.time constructors for PersonIdentIvan Frade2024-11-191-27/+44
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Stop using the deprecated constructor with long/int for time/tz and use instead Instant/ZoneId. The code is still using the old constructor, but this verifies that we are creating identical instances. We will update the code later. Change-Id: I3844bf1e790e53e69a894cd697bddb31b755c2e6
| * | | | | RawParseUtils test: Use java.time to create PersonIdentsIvan Frade2024-11-191-16/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The constructor with long/int for time/tz is deprecated in favor of Instant/ZoneId. Update first the expectations in the tests to the new constructors, so we know we are creating the same PersonIdents than before. We update the code later, knowing there is no regression in e.g. format or precision. Change-Id: I05c759bdd4cf73b8afe79fae3c792f2f1300d1e6
* | | | | | Merge "Change default similarity score to 50(%) to match git's default"Matthias Sohn2024-11-201-1/+1
|\ \ \ \ \ \ | |/ / / / / |/| | | | |